Merge remote-tracking branch 'origin/master' into 0324-merge-release-50-back-to-master
This commit is contained in:
commit
b37f186142
|
@ -18,7 +18,7 @@ services:
|
||||||
- /tmp/emqx-ci/emqx-shared-secret:/var/lib/secret
|
- /tmp/emqx-ci/emqx-shared-secret:/var/lib/secret
|
||||||
kdc:
|
kdc:
|
||||||
hostname: kdc.emqx.net
|
hostname: kdc.emqx.net
|
||||||
image: ghcr.io/emqx/emqx-builder/5.0-28:1.13.4-24.3.4.2-2-ubuntu20.04
|
image: ghcr.io/emqx/emqx-builder/5.0-33:1.13.4-24.3.4.2-3-ubuntu20.04
|
||||||
container_name: kdc.emqx.net
|
container_name: kdc.emqx.net
|
||||||
expose:
|
expose:
|
||||||
- 88 # kdc
|
- 88 # kdc
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
version: '3.9'
|
||||||
|
|
||||||
|
services:
|
||||||
|
mqnamesrv:
|
||||||
|
image: apache/rocketmq:4.9.4
|
||||||
|
container_name: rocketmq_namesrv
|
||||||
|
# ports:
|
||||||
|
# - 9876:9876
|
||||||
|
volumes:
|
||||||
|
- ./rocketmq/logs:/opt/logs
|
||||||
|
- ./rocketmq/store:/opt/store
|
||||||
|
command: ./mqnamesrv
|
||||||
|
networks:
|
||||||
|
- emqx_bridge
|
||||||
|
|
||||||
|
mqbroker:
|
||||||
|
image: apache/rocketmq:4.9.4
|
||||||
|
container_name: rocketmq_broker
|
||||||
|
# ports:
|
||||||
|
# - 10909:10909
|
||||||
|
# - 10911:10911
|
||||||
|
volumes:
|
||||||
|
- ./rocketmq/logs:/opt/logs
|
||||||
|
- ./rocketmq/store:/opt/store
|
||||||
|
- ./rocketmq/conf/broker.conf:/etc/rocketmq/broker.conf
|
||||||
|
environment:
|
||||||
|
NAMESRV_ADDR: "rocketmq_namesrv:9876"
|
||||||
|
JAVA_OPTS: " -Duser.home=/opt"
|
||||||
|
JAVA_OPT_EXT: "-server -Xms1024m -Xmx1024m -Xmn1024m"
|
||||||
|
command: ./mqbroker -c /etc/rocketmq/broker.conf
|
||||||
|
depends_on:
|
||||||
|
- mqnamesrv
|
||||||
|
networks:
|
||||||
|
- emqx_bridge
|
|
@ -22,6 +22,7 @@ services:
|
||||||
- 15433:5433
|
- 15433:5433
|
||||||
- 16041:6041
|
- 16041:6041
|
||||||
- 18000:8000
|
- 18000:8000
|
||||||
|
- 19876:9876
|
||||||
command:
|
command:
|
||||||
- "-host=0.0.0.0"
|
- "-host=0.0.0.0"
|
||||||
- "-config=/config/toxiproxy.json"
|
- "-config=/config/toxiproxy.json"
|
||||||
|
|
|
@ -3,7 +3,7 @@ version: '3.9'
|
||||||
services:
|
services:
|
||||||
erlang:
|
erlang:
|
||||||
container_name: erlang
|
container_name: erlang
|
||||||
image: ${DOCKER_CT_RUNNER_IMAGE:-ghcr.io/emqx/emqx-builder/5.0-28:1.13.4-24.3.4.2-2-ubuntu20.04}
|
image: ${DOCKER_CT_RUNNER_IMAGE:-ghcr.io/emqx/emqx-builder/5.0-33:1.13.4-24.3.4.2-3-ubuntu20.04}
|
||||||
env_file:
|
env_file:
|
||||||
- conf.env
|
- conf.env
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
brokerClusterName=DefaultCluster
|
||||||
|
brokerName=broker-a
|
||||||
|
brokerId=0
|
||||||
|
|
||||||
|
brokerIP1=rocketmq_broker
|
||||||
|
|
||||||
|
defaultTopicQueueNums=4
|
||||||
|
autoCreateTopicEnable=true
|
||||||
|
autoCreateSubscriptionGroup=true
|
||||||
|
|
||||||
|
listenPort=10911
|
||||||
|
deleteWhen=04
|
||||||
|
|
||||||
|
fileReservedTime=120
|
||||||
|
mapedFileSizeCommitLog=1073741824
|
||||||
|
mapedFileSizeConsumeQueue=300000
|
||||||
|
diskMaxUsedSpaceRatio=100
|
||||||
|
maxMessageSize=65536
|
||||||
|
|
||||||
|
brokerRole=ASYNC_MASTER
|
||||||
|
|
||||||
|
flushDiskType=ASYNC_FLUSH
|
|
@ -77,5 +77,11 @@
|
||||||
"listen": "0.0.0.0:9295",
|
"listen": "0.0.0.0:9295",
|
||||||
"upstream": "kafka-1.emqx.net:9295",
|
"upstream": "kafka-1.emqx.net:9295",
|
||||||
"enabled": true
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "rocketmq",
|
||||||
|
"listen": "0.0.0.0:9876",
|
||||||
|
"upstream": "rocketmq_namesrv:9876",
|
||||||
|
"enabled": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -25,7 +25,7 @@ jobs:
|
||||||
prepare:
|
prepare:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
# prepare source with any OTP version, no need for a matrix
|
# prepare source with any OTP version, no need for a matrix
|
||||||
container: "ghcr.io/emqx/emqx-builder/5.0-32:1.13.4-24.3.4.2-2-ubuntu22.04"
|
container: "ghcr.io/emqx/emqx-builder/5.0-33:1.13.4-24.3.4.2-3-ubuntu22.04"
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
PROFILE: ${{ steps.get_profile.outputs.PROFILE }}
|
PROFILE: ${{ steps.get_profile.outputs.PROFILE }}
|
||||||
|
@ -121,9 +121,9 @@ jobs:
|
||||||
# NOTE: 'otp' and 'elixir' are to configure emqx-builder image
|
# NOTE: 'otp' and 'elixir' are to configure emqx-builder image
|
||||||
# only support latest otp and elixir, not a matrix
|
# only support latest otp and elixir, not a matrix
|
||||||
builder:
|
builder:
|
||||||
- 5.0-32 # update to latest
|
- 5.0-33 # update to latest
|
||||||
otp:
|
otp:
|
||||||
- 24.3.4.2-2 # switch to 25 once ready to release 5.1
|
- 24.3.4.2-3 # switch to 25 once ready to release 5.1
|
||||||
elixir:
|
elixir:
|
||||||
- 'no_elixir'
|
- 'no_elixir'
|
||||||
- '1.13.4' # update to latest
|
- '1.13.4' # update to latest
|
||||||
|
|
|
@ -24,7 +24,7 @@ jobs:
|
||||||
prepare:
|
prepare:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
if: (github.repository_owner == 'emqx' && github.event_name == 'schedule') || github.event_name != 'schedule'
|
if: (github.repository_owner == 'emqx' && github.event_name == 'schedule') || github.event_name != 'schedule'
|
||||||
container: ghcr.io/emqx/emqx-builder/5.0-32:1.13.4-24.3.4.2-2-ubuntu22.04
|
container: ghcr.io/emqx/emqx-builder/5.0-33:1.13.4-24.3.4.2-3-ubuntu22.04
|
||||||
outputs:
|
outputs:
|
||||||
BUILD_PROFILE: ${{ steps.get_profile.outputs.BUILD_PROFILE }}
|
BUILD_PROFILE: ${{ steps.get_profile.outputs.BUILD_PROFILE }}
|
||||||
IS_EXACT_TAG: ${{ steps.get_profile.outputs.IS_EXACT_TAG }}
|
IS_EXACT_TAG: ${{ steps.get_profile.outputs.IS_EXACT_TAG }}
|
||||||
|
@ -151,7 +151,7 @@ jobs:
|
||||||
profile:
|
profile:
|
||||||
- ${{ needs.prepare.outputs.BUILD_PROFILE }}
|
- ${{ needs.prepare.outputs.BUILD_PROFILE }}
|
||||||
otp:
|
otp:
|
||||||
- 24.3.4.2-2
|
- 24.3.4.2-3
|
||||||
os:
|
os:
|
||||||
- macos-11
|
- macos-11
|
||||||
- macos-12
|
- macos-12
|
||||||
|
@ -203,7 +203,7 @@ jobs:
|
||||||
profile:
|
profile:
|
||||||
- ${{ needs.prepare.outputs.BUILD_PROFILE }}
|
- ${{ needs.prepare.outputs.BUILD_PROFILE }}
|
||||||
otp:
|
otp:
|
||||||
- 24.3.4.2-2
|
- 24.3.4.2-3
|
||||||
arch:
|
arch:
|
||||||
- amd64
|
- amd64
|
||||||
- arm64
|
- arm64
|
||||||
|
@ -221,7 +221,7 @@ jobs:
|
||||||
- aws-arm64
|
- aws-arm64
|
||||||
- ubuntu-22.04
|
- ubuntu-22.04
|
||||||
builder:
|
builder:
|
||||||
- 5.0-32
|
- 5.0-33
|
||||||
elixir:
|
elixir:
|
||||||
- 1.13.4
|
- 1.13.4
|
||||||
exclude:
|
exclude:
|
||||||
|
@ -231,19 +231,19 @@ jobs:
|
||||||
build_machine: aws-arm64
|
build_machine: aws-arm64
|
||||||
include:
|
include:
|
||||||
- profile: emqx
|
- profile: emqx
|
||||||
otp: 25.1.2-2
|
otp: 25.1.2-3
|
||||||
arch: amd64
|
arch: amd64
|
||||||
os: ubuntu22.04
|
os: ubuntu22.04
|
||||||
build_machine: ubuntu-22.04
|
build_machine: ubuntu-22.04
|
||||||
builder: 5.0-32
|
builder: 5.0-33
|
||||||
elixir: 1.13.4
|
elixir: 1.13.4
|
||||||
release_with: elixir
|
release_with: elixir
|
||||||
- profile: emqx
|
- profile: emqx
|
||||||
otp: 25.1.2-2
|
otp: 25.1.2-3
|
||||||
arch: amd64
|
arch: amd64
|
||||||
os: amzn2
|
os: amzn2
|
||||||
build_machine: ubuntu-22.04
|
build_machine: ubuntu-22.04
|
||||||
builder: 5.0-32
|
builder: 5.0-33
|
||||||
elixir: 1.13.4
|
elixir: 1.13.4
|
||||||
release_with: elixir
|
release_with: elixir
|
||||||
|
|
||||||
|
|
|
@ -30,12 +30,12 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
profile:
|
profile:
|
||||||
- ["emqx", "24.3.4.2-2", "el7", "erlang"]
|
- ["emqx", "24.3.4.2-3", "el7", "erlang"]
|
||||||
- ["emqx", "25.1.2-2", "ubuntu22.04", "elixir"]
|
- ["emqx", "25.1.2-3", "ubuntu22.04", "elixir"]
|
||||||
- ["emqx-enterprise", "24.3.4.2-2", "amzn2", "erlang"]
|
- ["emqx-enterprise", "24.3.4.2-3", "amzn2", "erlang"]
|
||||||
- ["emqx-enterprise", "25.1.2-2", "ubuntu20.04", "erlang"]
|
- ["emqx-enterprise", "25.1.2-3", "ubuntu20.04", "erlang"]
|
||||||
builder:
|
builder:
|
||||||
- 5.0-32
|
- 5.0-33
|
||||||
elixir:
|
elixir:
|
||||||
- '1.13.4'
|
- '1.13.4'
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ jobs:
|
||||||
- emqx
|
- emqx
|
||||||
- emqx-enterprise
|
- emqx-enterprise
|
||||||
otp:
|
otp:
|
||||||
- 24.3.4.2-2
|
- 24.3.4.2-3
|
||||||
os:
|
os:
|
||||||
- macos-11
|
- macos-11
|
||||||
- macos-12-arm64
|
- macos-12-arm64
|
||||||
|
|
|
@ -6,7 +6,7 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
check_deps_integrity:
|
check_deps_integrity:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
container: ghcr.io/emqx/emqx-builder/5.0-32:1.13.4-25.1.2-2-ubuntu22.04
|
container: ghcr.io/emqx/emqx-builder/5.0-33:1.13.4-25.1.2-3-ubuntu22.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
|
@ -5,7 +5,7 @@ on: [pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
code_style_check:
|
code_style_check:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
container: "ghcr.io/emqx/emqx-builder/5.0-32:1.13.4-25.1.2-2-ubuntu22.04"
|
container: "ghcr.io/emqx/emqx-builder/5.0-33:1.13.4-25.1.2-3-ubuntu22.04"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
||||||
elixir_apps_check:
|
elixir_apps_check:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
# just use the latest builder
|
# just use the latest builder
|
||||||
container: "ghcr.io/emqx/emqx-builder/5.0-32:1.13.4-25.1.2-2-ubuntu22.04"
|
container: "ghcr.io/emqx/emqx-builder/5.0-33:1.13.4-25.1.2-3-ubuntu22.04"
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
|
@ -8,7 +8,7 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
elixir_deps_check:
|
elixir_deps_check:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
container: ghcr.io/emqx/emqx-builder/5.0-32:1.13.4-25.1.2-2-ubuntu22.04
|
container: ghcr.io/emqx/emqx-builder/5.0-33:1.13.4-25.1.2-3-ubuntu22.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|
|
@ -17,7 +17,7 @@ jobs:
|
||||||
profile:
|
profile:
|
||||||
- emqx
|
- emqx
|
||||||
- emqx-enterprise
|
- emqx-enterprise
|
||||||
container: ghcr.io/emqx/emqx-builder/5.0-32:1.13.4-25.1.2-2-ubuntu22.04
|
container: ghcr.io/emqx/emqx-builder/5.0-33:1.13.4-25.1.2-3-ubuntu22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
name: Keep master green
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# run hourly
|
||||||
|
- cron: "0 * * * *"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
rerun-failed-jobs:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
if: github.repository_owner == 'emqx'
|
||||||
|
permissions:
|
||||||
|
checks: read
|
||||||
|
actions: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: run script
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
python3 scripts/rerun-failed-checks.py
|
|
@ -54,7 +54,7 @@ jobs:
|
||||||
OUTPUT_DIR=${{ steps.profile.outputs.s3dir }}
|
OUTPUT_DIR=${{ steps.profile.outputs.s3dir }}
|
||||||
aws s3 cp --recursive s3://$BUCKET/$OUTPUT_DIR/${{ github.ref_name }} packages
|
aws s3 cp --recursive s3://$BUCKET/$OUTPUT_DIR/${{ github.ref_name }} packages
|
||||||
cd packages
|
cd packages
|
||||||
DEFAULT_BEAM_PLATFORM='otp24.3.4.2-2'
|
DEFAULT_BEAM_PLATFORM='otp24.3.4.2-3'
|
||||||
# all packages including full-name and default-name are uploaded to s3
|
# all packages including full-name and default-name are uploaded to s3
|
||||||
# but we only upload default-name packages (and elixir) as github artifacts
|
# but we only upload default-name packages (and elixir) as github artifacts
|
||||||
# so we rename (overwrite) non-default packages before uploading
|
# so we rename (overwrite) non-default packages before uploading
|
||||||
|
|
|
@ -12,10 +12,10 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
builder:
|
builder:
|
||||||
- 5.0-32
|
- 5.0-33
|
||||||
otp:
|
otp:
|
||||||
- 24.3.4.2-2
|
- 24.3.4.2-3
|
||||||
- 25.1.2-2
|
- 25.1.2-3
|
||||||
# no need to use more than 1 version of Elixir, since tests
|
# no need to use more than 1 version of Elixir, since tests
|
||||||
# run using only Erlang code. This is needed just to specify
|
# run using only Erlang code. This is needed just to specify
|
||||||
# the base image.
|
# the base image.
|
||||||
|
|
|
@ -17,7 +17,7 @@ jobs:
|
||||||
prepare:
|
prepare:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
# prepare source with any OTP version, no need for a matrix
|
# prepare source with any OTP version, no need for a matrix
|
||||||
container: ghcr.io/emqx/emqx-builder/5.0-32:1.13.4-24.3.4.2-2-debian11
|
container: ghcr.io/emqx/emqx-builder/5.0-33:1.13.4-24.3.4.2-3-debian11
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
@ -50,9 +50,9 @@ jobs:
|
||||||
os:
|
os:
|
||||||
- ["debian11", "debian:11-slim"]
|
- ["debian11", "debian:11-slim"]
|
||||||
builder:
|
builder:
|
||||||
- 5.0-32
|
- 5.0-33
|
||||||
otp:
|
otp:
|
||||||
- 24.3.4.2-2
|
- 24.3.4.2-3
|
||||||
elixir:
|
elixir:
|
||||||
- 1.13.4
|
- 1.13.4
|
||||||
arch:
|
arch:
|
||||||
|
@ -123,9 +123,9 @@ jobs:
|
||||||
os:
|
os:
|
||||||
- ["debian11", "debian:11-slim"]
|
- ["debian11", "debian:11-slim"]
|
||||||
builder:
|
builder:
|
||||||
- 5.0-32
|
- 5.0-33
|
||||||
otp:
|
otp:
|
||||||
- 24.3.4.2-2
|
- 24.3.4.2-3
|
||||||
elixir:
|
elixir:
|
||||||
- 1.13.4
|
- 1.13.4
|
||||||
arch:
|
arch:
|
||||||
|
|
|
@ -15,7 +15,7 @@ concurrency:
|
||||||
jobs:
|
jobs:
|
||||||
relup_test_plan:
|
relup_test_plan:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
container: "ghcr.io/emqx/emqx-builder/5.0-32:1.13.4-24.3.4.2-2-ubuntu22.04"
|
container: "ghcr.io/emqx/emqx-builder/5.0-33:1.13.4-24.3.4.2-3-ubuntu22.04"
|
||||||
outputs:
|
outputs:
|
||||||
CUR_EE_VSN: ${{ steps.find-versions.outputs.CUR_EE_VSN }}
|
CUR_EE_VSN: ${{ steps.find-versions.outputs.CUR_EE_VSN }}
|
||||||
OLD_VERSIONS: ${{ steps.find-versions.outputs.OLD_VERSIONS }}
|
OLD_VERSIONS: ${{ steps.find-versions.outputs.OLD_VERSIONS }}
|
||||||
|
|
|
@ -31,13 +31,13 @@ jobs:
|
||||||
MATRIX="$(echo "${APPS}" | jq -c '
|
MATRIX="$(echo "${APPS}" | jq -c '
|
||||||
[
|
[
|
||||||
(.[] | select(.profile == "emqx") | . + {
|
(.[] | select(.profile == "emqx") | . + {
|
||||||
builder: "5.0-32",
|
builder: "5.0-33",
|
||||||
otp: "25.1.2-2",
|
otp: "25.1.2-3",
|
||||||
elixir: "1.13.4"
|
elixir: "1.13.4"
|
||||||
}),
|
}),
|
||||||
(.[] | select(.profile == "emqx-enterprise") | . + {
|
(.[] | select(.profile == "emqx-enterprise") | . + {
|
||||||
builder: "5.0-32",
|
builder: "5.0-33",
|
||||||
otp: ["24.3.4.2-2", "25.1.2-2"][],
|
otp: ["24.3.4.2-3", "25.1.2-3"][],
|
||||||
elixir: "1.13.4"
|
elixir: "1.13.4"
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
@ -230,12 +230,12 @@ jobs:
|
||||||
- ct
|
- ct
|
||||||
- ct_docker
|
- ct_docker
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
container: "ghcr.io/emqx/emqx-builder/5.0-32:1.13.4-24.3.4.2-2-ubuntu22.04"
|
container: "ghcr.io/emqx/emqx-builder/5.0-33:1.13.4-24.3.4.2-3-ubuntu22.04"
|
||||||
steps:
|
steps:
|
||||||
- uses: AutoModality/action-clean@v1
|
- uses: AutoModality/action-clean@v1
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: source-emqx-enterprise-24.3.4.2-2
|
name: source-emqx-enterprise-24.3.4.2-3
|
||||||
path: .
|
path: .
|
||||||
- name: unzip source code
|
- name: unzip source code
|
||||||
run: unzip -q source.zip
|
run: unzip -q source.zip
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
erlang 24.3.4.2-2
|
erlang 24.3.4.2-3
|
||||||
elixir 1.13.4-otp-24
|
elixir 1.13.4-otp-24
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -152,6 +152,7 @@ $(PROFILES:%=clean-%):
|
||||||
.PHONY: clean-all
|
.PHONY: clean-all
|
||||||
clean-all:
|
clean-all:
|
||||||
@rm -f rebar.lock
|
@rm -f rebar.lock
|
||||||
|
@rm -rf deps
|
||||||
@rm -rf _build
|
@rm -rf _build
|
||||||
|
|
||||||
.PHONY: deps-all
|
.PHONY: deps-all
|
||||||
|
|
|
@ -1810,6 +1810,56 @@ server_ssl_opts_schema_ocsp_refresh_http_timeout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server_ssl_opts_schema_enable_crl_check {
|
||||||
|
desc {
|
||||||
|
en: "Whether to enable CRL verification for this listener."
|
||||||
|
zh: "是否为该监听器启用 CRL 检查。"
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: "Enable CRL Check"
|
||||||
|
zh: "启用 CRL 检查"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crl_cache_refresh_http_timeout {
|
||||||
|
desc {
|
||||||
|
en: "The timeout for the HTTP request when fetching CRLs. This is"
|
||||||
|
" a global setting for all listeners."
|
||||||
|
zh: "获取 CRLs 时 HTTP 请求的超时。 该配置对所有启用 CRL 检查的监听器监听器有效。"
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: "CRL Cache Refresh HTTP Timeout"
|
||||||
|
zh: "CRL 缓存刷新 HTTP 超时"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crl_cache_refresh_interval {
|
||||||
|
desc {
|
||||||
|
en: "The period to refresh the CRLs from the servers. This is a global setting"
|
||||||
|
" for all URLs and listeners."
|
||||||
|
zh: "从服务器刷新CRL的周期。 该配置对所有 URL 和监听器有效。"
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: "CRL Cache Refresh Interval"
|
||||||
|
zh: "CRL 缓存刷新间隔"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crl_cache_capacity {
|
||||||
|
desc {
|
||||||
|
en: "The maximum number of CRL URLs that can be held in cache. If the cache is at"
|
||||||
|
" full capacity and a new URL must be fetched, then it'll evict the oldest"
|
||||||
|
" inserted URL in the cache."
|
||||||
|
zh: "缓存中可容纳的 CRL URL 的最大数量。"
|
||||||
|
" 如果缓存的容量已满,并且必须获取一个新的 URL,"
|
||||||
|
"那么它将驱逐缓存中插入的最老的 URL。"
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: "CRL Cache Capacity"
|
||||||
|
zh: "CRL 缓存容量"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fields_listeners_tcp {
|
fields_listeners_tcp {
|
||||||
desc {
|
desc {
|
||||||
en: """TCP listeners."""
|
en: """TCP listeners."""
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
{gproc, {git, "https://github.com/uwiger/gproc", {tag, "0.8.0"}}},
|
{gproc, {git, "https://github.com/uwiger/gproc", {tag, "0.8.0"}}},
|
||||||
{jiffy, {git, "https://github.com/emqx/jiffy", {tag, "1.0.5"}}},
|
{jiffy, {git, "https://github.com/emqx/jiffy", {tag, "1.0.5"}}},
|
||||||
{cowboy, {git, "https://github.com/emqx/cowboy", {tag, "2.9.0"}}},
|
{cowboy, {git, "https://github.com/emqx/cowboy", {tag, "2.9.0"}}},
|
||||||
{esockd, {git, "https://github.com/emqx/esockd", {tag, "5.9.4"}}},
|
{esockd, {git, "https://github.com/emqx/esockd", {tag, "5.9.6"}}},
|
||||||
{ekka, {git, "https://github.com/emqx/ekka", {tag, "0.14.5"}}},
|
{ekka, {git, "https://github.com/emqx/ekka", {tag, "0.14.6"}}},
|
||||||
{gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.8.1"}}},
|
{gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.8.1"}}},
|
||||||
{hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.37.2"}}},
|
{hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.37.2"}}},
|
||||||
{emqx_http_lib, {git, "https://github.com/emqx/emqx_http_lib.git", {tag, "0.5.2"}}},
|
{emqx_http_lib, {git, "https://github.com/emqx/emqx_http_lib.git", {tag, "0.5.2"}}},
|
||||||
|
@ -59,4 +59,12 @@
|
||||||
{statistics, true}
|
{statistics, true}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{project_plugins, [erlfmt]}.
|
{project_plugins, [
|
||||||
|
{erlfmt, [
|
||||||
|
{files, [
|
||||||
|
"{src,include,test}/*.{hrl,erl,app.src}",
|
||||||
|
"rebar.config",
|
||||||
|
"rebar.config.script"
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]}.
|
||||||
|
|
|
@ -24,20 +24,20 @@ IsQuicSupp = fun() ->
|
||||||
end,
|
end,
|
||||||
|
|
||||||
Bcrypt = {bcrypt, {git, "https://github.com/emqx/erlang-bcrypt.git", {tag, "0.6.0"}}},
|
Bcrypt = {bcrypt, {git, "https://github.com/emqx/erlang-bcrypt.git", {tag, "0.6.0"}}},
|
||||||
Quicer = {quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.113"}}}.
|
Quicer = {quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.114"}}}.
|
||||||
|
|
||||||
Dialyzer = fun(Config) ->
|
Dialyzer = fun(Config) ->
|
||||||
{dialyzer, OldDialyzerConfig} = lists:keyfind(dialyzer, 1, Config),
|
{dialyzer, OldDialyzerConfig} = lists:keyfind(dialyzer, 1, Config),
|
||||||
{plt_extra_apps, OldExtra} = lists:keyfind(plt_extra_apps, 1, OldDialyzerConfig),
|
{plt_extra_apps, OldExtra} = lists:keyfind(plt_extra_apps, 1, OldDialyzerConfig),
|
||||||
Extra = OldExtra ++ [quicer || IsQuicSupp()],
|
Extra = OldExtra ++ [quicer || IsQuicSupp()],
|
||||||
NewDialyzerConfig = [{plt_extra_apps, Extra} | OldDialyzerConfig],
|
NewDialyzerConfig = [{plt_extra_apps, Extra} | OldDialyzerConfig],
|
||||||
lists:keystore(
|
lists:keystore(
|
||||||
dialyzer,
|
dialyzer,
|
||||||
1,
|
1,
|
||||||
Config,
|
Config,
|
||||||
{dialyzer, NewDialyzerConfig}
|
{dialyzer, NewDialyzerConfig}
|
||||||
)
|
)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
ExtraDeps = fun(C) ->
|
ExtraDeps = fun(C) ->
|
||||||
{deps, Deps0} = lists:keyfind(deps, 1, C),
|
{deps, Deps0} = lists:keyfind(deps, 1, C),
|
||||||
|
|
|
@ -2128,17 +2128,23 @@ publish_will_msg(
|
||||||
ClientInfo = #{mountpoint := MountPoint},
|
ClientInfo = #{mountpoint := MountPoint},
|
||||||
Msg = #message{topic = Topic}
|
Msg = #message{topic = Topic}
|
||||||
) ->
|
) ->
|
||||||
case emqx_access_control:authorize(ClientInfo, publish, Topic) of
|
PublishingDisallowed = emqx_access_control:authorize(ClientInfo, publish, Topic) =/= allow,
|
||||||
allow ->
|
ClientBanned = emqx_banned:check(ClientInfo),
|
||||||
NMsg = emqx_mountpoint:mount(MountPoint, Msg),
|
case PublishingDisallowed orelse ClientBanned of
|
||||||
_ = emqx_broker:publish(NMsg),
|
true ->
|
||||||
ok;
|
|
||||||
deny ->
|
|
||||||
?tp(
|
?tp(
|
||||||
warning,
|
warning,
|
||||||
last_will_testament_publish_denied,
|
last_will_testament_publish_denied,
|
||||||
#{topic => Topic}
|
#{
|
||||||
|
topic => Topic,
|
||||||
|
client_banned => ClientBanned,
|
||||||
|
publishing_disallowed => PublishingDisallowed
|
||||||
|
}
|
||||||
),
|
),
|
||||||
|
ok;
|
||||||
|
false ->
|
||||||
|
NMsg = emqx_mountpoint:mount(MountPoint, Msg),
|
||||||
|
_ = emqx_broker:publish(NMsg),
|
||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,314 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2022-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
%% you may not use this file except in compliance with the License.
|
||||||
|
%% You may obtain a copy of the License at
|
||||||
|
%%
|
||||||
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
%%
|
||||||
|
%% Unless required by applicable law or agreed to in writing, software
|
||||||
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
%% See the License for the specific language governing permissions and
|
||||||
|
%% limitations under the License.
|
||||||
|
%%
|
||||||
|
%% @doc EMQX CRL cache.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_crl_cache).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([
|
||||||
|
start_link/0,
|
||||||
|
start_link/1,
|
||||||
|
register_der_crls/2,
|
||||||
|
refresh/1,
|
||||||
|
evict/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
%% gen_server callbacks
|
||||||
|
-export([
|
||||||
|
init/1,
|
||||||
|
handle_call/3,
|
||||||
|
handle_cast/2,
|
||||||
|
handle_info/2
|
||||||
|
]).
|
||||||
|
|
||||||
|
%% internal exports
|
||||||
|
-export([http_get/2]).
|
||||||
|
|
||||||
|
-behaviour(gen_server).
|
||||||
|
|
||||||
|
-include("logger.hrl").
|
||||||
|
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
||||||
|
|
||||||
|
-define(HTTP_TIMEOUT, timer:seconds(15)).
|
||||||
|
-define(RETRY_TIMEOUT, 5_000).
|
||||||
|
-ifdef(TEST).
|
||||||
|
-define(MIN_REFRESH_PERIOD, timer:seconds(5)).
|
||||||
|
-else.
|
||||||
|
-define(MIN_REFRESH_PERIOD, timer:minutes(1)).
|
||||||
|
-endif.
|
||||||
|
-define(DEFAULT_REFRESH_INTERVAL, timer:minutes(15)).
|
||||||
|
-define(DEFAULT_CACHE_CAPACITY, 100).
|
||||||
|
|
||||||
|
-record(state, {
|
||||||
|
refresh_timers = #{} :: #{binary() => timer:tref()},
|
||||||
|
refresh_interval = timer:minutes(15) :: timer:time(),
|
||||||
|
http_timeout = ?HTTP_TIMEOUT :: timer:time(),
|
||||||
|
%% keeps track of URLs by insertion time
|
||||||
|
insertion_times = gb_trees:empty() :: gb_trees:tree(timer:time(), url()),
|
||||||
|
%% the set of cached URLs, for testing if an URL is already
|
||||||
|
%% registered.
|
||||||
|
cached_urls = sets:new([{version, 2}]) :: sets:set(url()),
|
||||||
|
cache_capacity = 100 :: pos_integer(),
|
||||||
|
%% for future use
|
||||||
|
extra = #{} :: map()
|
||||||
|
}).
|
||||||
|
-type url() :: uri_string:uri_string().
|
||||||
|
-type state() :: #state{}.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% API
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
start_link() ->
|
||||||
|
Config = gather_config(),
|
||||||
|
start_link(Config).
|
||||||
|
|
||||||
|
start_link(Config = #{cache_capacity := _, refresh_interval := _, http_timeout := _}) ->
|
||||||
|
gen_server:start_link({local, ?MODULE}, ?MODULE, Config, []).
|
||||||
|
|
||||||
|
-spec refresh(url()) -> ok.
|
||||||
|
refresh(URL) ->
|
||||||
|
gen_server:cast(?MODULE, {refresh, URL}).
|
||||||
|
|
||||||
|
-spec evict(url()) -> ok.
|
||||||
|
evict(URL) ->
|
||||||
|
gen_server:cast(?MODULE, {evict, URL}).
|
||||||
|
|
||||||
|
%% Adds CRLs in DER format to the cache and register them for periodic
|
||||||
|
%% refresh.
|
||||||
|
-spec register_der_crls(url(), [public_key:der_encoded()]) -> ok.
|
||||||
|
register_der_crls(URL, CRLs) when is_list(CRLs) ->
|
||||||
|
gen_server:cast(?MODULE, {register_der_crls, URL, CRLs}).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% gen_server behaviour
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
init(Config) ->
|
||||||
|
#{
|
||||||
|
cache_capacity := CacheCapacity,
|
||||||
|
refresh_interval := RefreshIntervalMS,
|
||||||
|
http_timeout := HTTPTimeoutMS
|
||||||
|
} = Config,
|
||||||
|
State = #state{
|
||||||
|
cache_capacity = CacheCapacity,
|
||||||
|
refresh_interval = RefreshIntervalMS,
|
||||||
|
http_timeout = HTTPTimeoutMS
|
||||||
|
},
|
||||||
|
{ok, State}.
|
||||||
|
|
||||||
|
handle_call(Call, _From, State) ->
|
||||||
|
{reply, {error, {bad_call, Call}}, State}.
|
||||||
|
|
||||||
|
handle_cast({evict, URL}, State0 = #state{refresh_timers = RefreshTimers0}) ->
|
||||||
|
emqx_ssl_crl_cache:delete(URL),
|
||||||
|
MTimer = maps:get(URL, RefreshTimers0, undefined),
|
||||||
|
emqx_misc:cancel_timer(MTimer),
|
||||||
|
RefreshTimers = maps:without([URL], RefreshTimers0),
|
||||||
|
State = State0#state{refresh_timers = RefreshTimers},
|
||||||
|
?tp(
|
||||||
|
crl_cache_evict,
|
||||||
|
#{url => URL}
|
||||||
|
),
|
||||||
|
{noreply, State};
|
||||||
|
handle_cast({register_der_crls, URL, CRLs}, State0) ->
|
||||||
|
handle_register_der_crls(State0, URL, CRLs);
|
||||||
|
handle_cast({refresh, URL}, State0) ->
|
||||||
|
case do_http_fetch_and_cache(URL, State0#state.http_timeout) of
|
||||||
|
{error, Error} ->
|
||||||
|
?tp(crl_refresh_failure, #{error => Error, url => URL}),
|
||||||
|
?SLOG(error, #{
|
||||||
|
msg => "failed_to_fetch_crl_response",
|
||||||
|
url => URL,
|
||||||
|
error => Error
|
||||||
|
}),
|
||||||
|
{noreply, ensure_timer(URL, State0, ?RETRY_TIMEOUT)};
|
||||||
|
{ok, _CRLs} ->
|
||||||
|
?SLOG(debug, #{
|
||||||
|
msg => "fetched_crl_response",
|
||||||
|
url => URL
|
||||||
|
}),
|
||||||
|
{noreply, ensure_timer(URL, State0)}
|
||||||
|
end;
|
||||||
|
handle_cast(_Cast, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
handle_info(
|
||||||
|
{timeout, TRef, {refresh, URL}},
|
||||||
|
State = #state{
|
||||||
|
refresh_timers = RefreshTimers,
|
||||||
|
http_timeout = HTTPTimeoutMS
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
case maps:get(URL, RefreshTimers, undefined) of
|
||||||
|
TRef ->
|
||||||
|
?tp(debug, crl_refresh_timer, #{url => URL}),
|
||||||
|
case do_http_fetch_and_cache(URL, HTTPTimeoutMS) of
|
||||||
|
{error, Error} ->
|
||||||
|
?SLOG(error, #{
|
||||||
|
msg => "failed_to_fetch_crl_response",
|
||||||
|
url => URL,
|
||||||
|
error => Error
|
||||||
|
}),
|
||||||
|
{noreply, ensure_timer(URL, State, ?RETRY_TIMEOUT)};
|
||||||
|
{ok, _CRLs} ->
|
||||||
|
?tp(debug, crl_refresh_timer_done, #{url => URL}),
|
||||||
|
{noreply, ensure_timer(URL, State)}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
{noreply, State}
|
||||||
|
end;
|
||||||
|
handle_info(_Info, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% internal functions
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
http_get(URL, HTTPTimeout) ->
|
||||||
|
httpc:request(
|
||||||
|
get,
|
||||||
|
{URL, [{"connection", "close"}]},
|
||||||
|
[{timeout, HTTPTimeout}],
|
||||||
|
[{body_format, binary}]
|
||||||
|
).
|
||||||
|
|
||||||
|
do_http_fetch_and_cache(URL, HTTPTimeoutMS) ->
|
||||||
|
?tp(crl_http_fetch, #{crl_url => URL}),
|
||||||
|
Resp = ?MODULE:http_get(URL, HTTPTimeoutMS),
|
||||||
|
case Resp of
|
||||||
|
{ok, {{_, 200, _}, _, Body}} ->
|
||||||
|
case parse_crls(Body) of
|
||||||
|
error ->
|
||||||
|
{error, invalid_crl};
|
||||||
|
CRLs ->
|
||||||
|
%% Note: must ensure it's a string and not a
|
||||||
|
%% binary because that's what the ssl manager uses
|
||||||
|
%% when doing lookups.
|
||||||
|
emqx_ssl_crl_cache:insert(to_string(URL), {der, CRLs}),
|
||||||
|
?tp(crl_cache_insert, #{url => URL, crls => CRLs}),
|
||||||
|
{ok, CRLs}
|
||||||
|
end;
|
||||||
|
{ok, {{_, Code, _}, _, Body}} ->
|
||||||
|
{error, {bad_response, #{code => Code, body => Body}}};
|
||||||
|
{error, Error} ->
|
||||||
|
{error, {http_error, Error}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
parse_crls(Bin) ->
|
||||||
|
try
|
||||||
|
[CRL || {'CertificateList', CRL, not_encrypted} <- public_key:pem_decode(Bin)]
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
ensure_timer(URL, State = #state{refresh_interval = Timeout}) ->
|
||||||
|
ensure_timer(URL, State, Timeout).
|
||||||
|
|
||||||
|
ensure_timer(URL, State = #state{refresh_timers = RefreshTimers0}, Timeout) ->
|
||||||
|
?tp(crl_cache_ensure_timer, #{url => URL, timeout => Timeout}),
|
||||||
|
MTimer = maps:get(URL, RefreshTimers0, undefined),
|
||||||
|
emqx_misc:cancel_timer(MTimer),
|
||||||
|
RefreshTimers = RefreshTimers0#{
|
||||||
|
URL => emqx_misc:start_timer(
|
||||||
|
Timeout,
|
||||||
|
{refresh, URL}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
State#state{refresh_timers = RefreshTimers}.
|
||||||
|
|
||||||
|
-spec gather_config() ->
|
||||||
|
#{
|
||||||
|
cache_capacity := pos_integer(),
|
||||||
|
refresh_interval := timer:time(),
|
||||||
|
http_timeout := timer:time()
|
||||||
|
}.
|
||||||
|
gather_config() ->
|
||||||
|
%% TODO: add a config handler to refresh the config when those
|
||||||
|
%% globals change?
|
||||||
|
CacheCapacity = emqx_config:get([crl_cache, capacity], ?DEFAULT_CACHE_CAPACITY),
|
||||||
|
RefreshIntervalMS0 = emqx_config:get([crl_cache, refresh_interval], ?DEFAULT_REFRESH_INTERVAL),
|
||||||
|
MinimumRefreshInverval = ?MIN_REFRESH_PERIOD,
|
||||||
|
RefreshIntervalMS = max(RefreshIntervalMS0, MinimumRefreshInverval),
|
||||||
|
HTTPTimeoutMS = emqx_config:get([crl_cache, http_timeout], ?HTTP_TIMEOUT),
|
||||||
|
#{
|
||||||
|
cache_capacity => CacheCapacity,
|
||||||
|
refresh_interval => RefreshIntervalMS,
|
||||||
|
http_timeout => HTTPTimeoutMS
|
||||||
|
}.
|
||||||
|
|
||||||
|
-spec handle_register_der_crls(state(), url(), [public_key:der_encoded()]) -> {noreply, state()}.
|
||||||
|
handle_register_der_crls(State0, URL0, CRLs) ->
|
||||||
|
#state{cached_urls = CachedURLs0} = State0,
|
||||||
|
URL = to_string(URL0),
|
||||||
|
case sets:is_element(URL, CachedURLs0) of
|
||||||
|
true ->
|
||||||
|
{noreply, State0};
|
||||||
|
false ->
|
||||||
|
emqx_ssl_crl_cache:insert(URL, {der, CRLs}),
|
||||||
|
?tp(debug, new_crl_url_inserted, #{url => URL}),
|
||||||
|
State1 = do_register_url(State0, URL),
|
||||||
|
State2 = handle_cache_overflow(State1),
|
||||||
|
State = ensure_timer(URL, State2),
|
||||||
|
{noreply, State}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec do_register_url(state(), url()) -> state().
|
||||||
|
do_register_url(State0, URL) ->
|
||||||
|
#state{
|
||||||
|
cached_urls = CachedURLs0,
|
||||||
|
insertion_times = InsertionTimes0
|
||||||
|
} = State0,
|
||||||
|
Now = erlang:monotonic_time(),
|
||||||
|
CachedURLs = sets:add_element(URL, CachedURLs0),
|
||||||
|
InsertionTimes = gb_trees:enter(Now, URL, InsertionTimes0),
|
||||||
|
State0#state{
|
||||||
|
cached_urls = CachedURLs,
|
||||||
|
insertion_times = InsertionTimes
|
||||||
|
}.
|
||||||
|
|
||||||
|
-spec handle_cache_overflow(state()) -> state().
|
||||||
|
handle_cache_overflow(State0) ->
|
||||||
|
#state{
|
||||||
|
cached_urls = CachedURLs0,
|
||||||
|
insertion_times = InsertionTimes0,
|
||||||
|
cache_capacity = CacheCapacity,
|
||||||
|
refresh_timers = RefreshTimers0
|
||||||
|
} = State0,
|
||||||
|
case sets:size(CachedURLs0) > CacheCapacity of
|
||||||
|
false ->
|
||||||
|
State0;
|
||||||
|
true ->
|
||||||
|
{_Time, OldestURL, InsertionTimes} = gb_trees:take_smallest(InsertionTimes0),
|
||||||
|
emqx_ssl_crl_cache:delete(OldestURL),
|
||||||
|
MTimer = maps:get(OldestURL, RefreshTimers0, undefined),
|
||||||
|
emqx_misc:cancel_timer(MTimer),
|
||||||
|
RefreshTimers = maps:remove(OldestURL, RefreshTimers0),
|
||||||
|
CachedURLs = sets:del_element(OldestURL, CachedURLs0),
|
||||||
|
?tp(debug, crl_cache_overflow, #{oldest_url => OldestURL}),
|
||||||
|
State0#state{
|
||||||
|
insertion_times = InsertionTimes,
|
||||||
|
cached_urls = CachedURLs,
|
||||||
|
refresh_timers = RefreshTimers
|
||||||
|
}
|
||||||
|
end.
|
||||||
|
|
||||||
|
to_string(B) when is_binary(B) ->
|
||||||
|
binary_to_list(B);
|
||||||
|
to_string(L) when is_list(L) ->
|
||||||
|
L.
|
|
@ -36,7 +36,8 @@ init([]) ->
|
||||||
child_spec(emqx_stats, worker),
|
child_spec(emqx_stats, worker),
|
||||||
child_spec(emqx_metrics, worker),
|
child_spec(emqx_metrics, worker),
|
||||||
child_spec(emqx_authn_authz_metrics_sup, supervisor),
|
child_spec(emqx_authn_authz_metrics_sup, supervisor),
|
||||||
child_spec(emqx_ocsp_cache, worker)
|
child_spec(emqx_ocsp_cache, worker),
|
||||||
|
child_spec(emqx_crl_cache, worker)
|
||||||
]
|
]
|
||||||
}}.
|
}}.
|
||||||
|
|
||||||
|
|
|
@ -487,7 +487,8 @@ esockd_opts(ListenerId, Type, Opts0) ->
|
||||||
tcp ->
|
tcp ->
|
||||||
Opts3#{tcp_options => tcp_opts(Opts0)};
|
Opts3#{tcp_options => tcp_opts(Opts0)};
|
||||||
ssl ->
|
ssl ->
|
||||||
OptsWithSNI = inject_sni_fun(ListenerId, Opts0),
|
OptsWithCRL = inject_crl_config(Opts0),
|
||||||
|
OptsWithSNI = inject_sni_fun(ListenerId, OptsWithCRL),
|
||||||
SSLOpts = ssl_opts(OptsWithSNI),
|
SSLOpts = ssl_opts(OptsWithSNI),
|
||||||
Opts3#{ssl_options => SSLOpts, tcp_options => tcp_opts(Opts0)}
|
Opts3#{ssl_options => SSLOpts, tcp_options => tcp_opts(Opts0)}
|
||||||
end
|
end
|
||||||
|
@ -794,3 +795,17 @@ inject_sni_fun(ListenerId, Conf = #{ssl_options := #{ocsp := #{enable_ocsp_stapl
|
||||||
emqx_ocsp_cache:inject_sni_fun(ListenerId, Conf);
|
emqx_ocsp_cache:inject_sni_fun(ListenerId, Conf);
|
||||||
inject_sni_fun(_ListenerId, Conf) ->
|
inject_sni_fun(_ListenerId, Conf) ->
|
||||||
Conf.
|
Conf.
|
||||||
|
|
||||||
|
inject_crl_config(
|
||||||
|
Conf = #{ssl_options := #{enable_crl_check := true} = SSLOpts}
|
||||||
|
) ->
|
||||||
|
HTTPTimeout = emqx_config:get([crl_cache, http_timeout], timer:seconds(15)),
|
||||||
|
Conf#{
|
||||||
|
ssl_options := SSLOpts#{
|
||||||
|
%% `crl_check => true' doesn't work
|
||||||
|
crl_check => peer,
|
||||||
|
crl_cache => {emqx_ssl_crl_cache, {internal, [{http, HTTPTimeout}]}}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
inject_crl_config(Conf) ->
|
||||||
|
Conf.
|
||||||
|
|
|
@ -545,10 +545,23 @@ readable_error_msg(Error) ->
|
||||||
{ok, Msg} ->
|
{ok, Msg} ->
|
||||||
Msg;
|
Msg;
|
||||||
false ->
|
false ->
|
||||||
iolist_to_binary(io_lib:format("~0p", [Error]))
|
to_hr_error(Error)
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
to_hr_error(nxdomain) ->
|
||||||
|
<<"Could not resolve host">>;
|
||||||
|
to_hr_error(econnrefused) ->
|
||||||
|
<<"Connection refused">>;
|
||||||
|
to_hr_error({unauthorized_client, _}) ->
|
||||||
|
<<"Unauthorized client">>;
|
||||||
|
to_hr_error({not_authorized, _}) ->
|
||||||
|
<<"Not authorized">>;
|
||||||
|
to_hr_error({malformed_username_or_password, _}) ->
|
||||||
|
<<"Bad username or password">>;
|
||||||
|
to_hr_error(Error) ->
|
||||||
|
iolist_to_binary(io_lib:format("~0p", [Error])).
|
||||||
|
|
||||||
try_to_existing_atom(Convert, Data, Encoding) ->
|
try_to_existing_atom(Convert, Data, Encoding) ->
|
||||||
try Convert(Data, Encoding) of
|
try Convert(Data, Encoding) of
|
||||||
Atom ->
|
Atom ->
|
||||||
|
|
|
@ -226,6 +226,11 @@ roots(low) ->
|
||||||
sc(
|
sc(
|
||||||
ref("trace"),
|
ref("trace"),
|
||||||
#{}
|
#{}
|
||||||
|
)},
|
||||||
|
{"crl_cache",
|
||||||
|
sc(
|
||||||
|
ref("crl_cache"),
|
||||||
|
#{hidden => true}
|
||||||
)}
|
)}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
@ -794,6 +799,37 @@ fields("listeners") ->
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
];
|
];
|
||||||
|
fields("crl_cache") ->
|
||||||
|
%% Note: we make the refresh interval and HTTP timeout global (not
|
||||||
|
%% per-listener) because multiple SSL listeners might point to the
|
||||||
|
%% same URL. If they had diverging timeout options, it would be
|
||||||
|
%% confusing.
|
||||||
|
[
|
||||||
|
{"refresh_interval",
|
||||||
|
sc(
|
||||||
|
duration(),
|
||||||
|
#{
|
||||||
|
default => <<"15m">>,
|
||||||
|
desc => ?DESC("crl_cache_refresh_interval")
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{"http_timeout",
|
||||||
|
sc(
|
||||||
|
duration(),
|
||||||
|
#{
|
||||||
|
default => <<"15s">>,
|
||||||
|
desc => ?DESC("crl_cache_refresh_http_timeout")
|
||||||
|
}
|
||||||
|
)},
|
||||||
|
{"capacity",
|
||||||
|
sc(
|
||||||
|
pos_integer(),
|
||||||
|
#{
|
||||||
|
default => 100,
|
||||||
|
desc => ?DESC("crl_cache_capacity")
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
];
|
||||||
fields("mqtt_tcp_listener") ->
|
fields("mqtt_tcp_listener") ->
|
||||||
mqtt_listener(1883) ++
|
mqtt_listener(1883) ++
|
||||||
[
|
[
|
||||||
|
@ -2065,6 +2101,8 @@ desc("shared_subscription_group") ->
|
||||||
"Per group dispatch strategy for shared subscription";
|
"Per group dispatch strategy for shared subscription";
|
||||||
desc("ocsp") ->
|
desc("ocsp") ->
|
||||||
"Per listener OCSP Stapling configuration.";
|
"Per listener OCSP Stapling configuration.";
|
||||||
|
desc("crl_cache") ->
|
||||||
|
"Global CRL cache options.";
|
||||||
desc(_) ->
|
desc(_) ->
|
||||||
undefined.
|
undefined.
|
||||||
|
|
||||||
|
@ -2264,13 +2302,22 @@ server_ssl_opts_schema(Defaults, IsRanchListener) ->
|
||||||
hidden => true,
|
hidden => true,
|
||||||
validator => fun ocsp_inner_validator/1
|
validator => fun ocsp_inner_validator/1
|
||||||
}
|
}
|
||||||
|
)},
|
||||||
|
{"enable_crl_check",
|
||||||
|
sc(
|
||||||
|
boolean(),
|
||||||
|
#{
|
||||||
|
default => false,
|
||||||
|
desc => ?DESC("server_ssl_opts_schema_enable_crl_check")
|
||||||
|
}
|
||||||
)}
|
)}
|
||||||
]
|
]
|
||||||
].
|
].
|
||||||
|
|
||||||
mqtt_ssl_listener_ssl_options_validator(Conf) ->
|
mqtt_ssl_listener_ssl_options_validator(Conf) ->
|
||||||
Checks = [
|
Checks = [
|
||||||
fun ocsp_outer_validator/1
|
fun ocsp_outer_validator/1,
|
||||||
|
fun crl_outer_validator/1
|
||||||
],
|
],
|
||||||
case emqx_misc:pipeline(Checks, Conf, not_used) of
|
case emqx_misc:pipeline(Checks, Conf, not_used) of
|
||||||
{ok, _, _} ->
|
{ok, _, _} ->
|
||||||
|
@ -2305,6 +2352,18 @@ ocsp_inner_validator(#{<<"enable_ocsp_stapling">> := true} = Conf) ->
|
||||||
),
|
),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
crl_outer_validator(
|
||||||
|
#{<<"enable_crl_check">> := true} = SSLOpts
|
||||||
|
) ->
|
||||||
|
case maps:get(<<"verify">>, SSLOpts) of
|
||||||
|
verify_peer ->
|
||||||
|
ok;
|
||||||
|
_ ->
|
||||||
|
{error, "verify must be verify_peer when CRL check is enabled"}
|
||||||
|
end;
|
||||||
|
crl_outer_validator(_SSLOpts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
%% @doc Make schema for SSL client.
|
%% @doc Make schema for SSL client.
|
||||||
-spec client_ssl_opts_schema(map()) -> hocon_schema:field_schema().
|
-spec client_ssl_opts_schema(map()) -> hocon_schema:field_schema().
|
||||||
client_ssl_opts_schema(Defaults) ->
|
client_ssl_opts_schema(Defaults) ->
|
||||||
|
|
|
@ -0,0 +1,237 @@
|
||||||
|
%%
|
||||||
|
%% %CopyrightBegin%
|
||||||
|
%%
|
||||||
|
%% Copyright Ericsson AB 2015-2022. 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.
|
||||||
|
%%
|
||||||
|
%% %CopyrightEnd%
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2023 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.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------
|
||||||
|
% Based on `otp/lib/ssl/src/ssl_crl_cache.erl'
|
||||||
|
%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
%----------------------------------------------------------------------
|
||||||
|
%% Purpose: Simple default CRL cache
|
||||||
|
%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_ssl_crl_cache).
|
||||||
|
|
||||||
|
-include_lib("ssl/src/ssl_internal.hrl").
|
||||||
|
-include_lib("public_key/include/public_key.hrl").
|
||||||
|
|
||||||
|
-behaviour(ssl_crl_cache_api).
|
||||||
|
|
||||||
|
-export_type([crl_src/0, uri/0]).
|
||||||
|
-type crl_src() :: {file, file:filename()} | {der, public_key:der_encoded()}.
|
||||||
|
-type uri() :: uri_string:uri_string().
|
||||||
|
|
||||||
|
-export([lookup/3, select/2, fresh_crl/2]).
|
||||||
|
-export([insert/1, insert/2, delete/1]).
|
||||||
|
|
||||||
|
%% Allow usage of OTP certificate record fields (camelCase).
|
||||||
|
-elvis([
|
||||||
|
{elvis_style, atom_naming_convention, #{
|
||||||
|
regex => "^([a-z][a-z0-9]*_?)([a-zA-Z0-9]*_?)*$",
|
||||||
|
enclosed_atoms => ".*"
|
||||||
|
}}
|
||||||
|
]).
|
||||||
|
|
||||||
|
%%====================================================================
|
||||||
|
%% Cache callback API
|
||||||
|
%%====================================================================
|
||||||
|
|
||||||
|
lookup(
|
||||||
|
#'DistributionPoint'{distributionPoint = {fullName, Names}},
|
||||||
|
_Issuer,
|
||||||
|
CRLDbInfo
|
||||||
|
) ->
|
||||||
|
get_crls(Names, CRLDbInfo);
|
||||||
|
lookup(_, _, _) ->
|
||||||
|
not_available.
|
||||||
|
|
||||||
|
select(GenNames, CRLDbHandle) when is_list(GenNames) ->
|
||||||
|
lists:flatmap(
|
||||||
|
fun
|
||||||
|
({directoryName, Issuer}) ->
|
||||||
|
select(Issuer, CRLDbHandle);
|
||||||
|
(_) ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
GenNames
|
||||||
|
);
|
||||||
|
select(Issuer, {{_Cache, Mapping}, _}) ->
|
||||||
|
case ssl_pkix_db:lookup(Issuer, Mapping) of
|
||||||
|
undefined ->
|
||||||
|
[];
|
||||||
|
CRLs ->
|
||||||
|
CRLs
|
||||||
|
end.
|
||||||
|
|
||||||
|
fresh_crl(#'DistributionPoint'{distributionPoint = {fullName, Names}}, CRL) ->
|
||||||
|
case get_crls(Names, undefined) of
|
||||||
|
not_available ->
|
||||||
|
CRL;
|
||||||
|
NewCRL ->
|
||||||
|
NewCRL
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%====================================================================
|
||||||
|
%% API
|
||||||
|
%%====================================================================
|
||||||
|
|
||||||
|
insert(CRLs) ->
|
||||||
|
insert(?NO_DIST_POINT, CRLs).
|
||||||
|
|
||||||
|
insert(URI, {file, File}) when is_list(URI) ->
|
||||||
|
case file:read_file(File) of
|
||||||
|
{ok, PemBin} ->
|
||||||
|
PemEntries = public_key:pem_decode(PemBin),
|
||||||
|
CRLs = [
|
||||||
|
CRL
|
||||||
|
|| {'CertificateList', CRL, not_encrypted} <-
|
||||||
|
PemEntries
|
||||||
|
],
|
||||||
|
do_insert(URI, CRLs);
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end;
|
||||||
|
insert(URI, {der, CRLs}) ->
|
||||||
|
do_insert(URI, CRLs).
|
||||||
|
|
||||||
|
delete({file, File}) ->
|
||||||
|
case file:read_file(File) of
|
||||||
|
{ok, PemBin} ->
|
||||||
|
PemEntries = public_key:pem_decode(PemBin),
|
||||||
|
CRLs = [
|
||||||
|
CRL
|
||||||
|
|| {'CertificateList', CRL, not_encrypted} <-
|
||||||
|
PemEntries
|
||||||
|
],
|
||||||
|
ssl_manager:delete_crls({?NO_DIST_POINT, CRLs});
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end;
|
||||||
|
delete({der, CRLs}) ->
|
||||||
|
ssl_manager:delete_crls({?NO_DIST_POINT, CRLs});
|
||||||
|
delete(URI) ->
|
||||||
|
case uri_string:normalize(URI, [return_map]) of
|
||||||
|
#{scheme := "http", path := Path} ->
|
||||||
|
ssl_manager:delete_crls(string:trim(Path, leading, "/"));
|
||||||
|
_ ->
|
||||||
|
{error, {only_http_distribution_points_supported, URI}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%%% Internal functions
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
do_insert(URI, CRLs) ->
|
||||||
|
case uri_string:normalize(URI, [return_map]) of
|
||||||
|
#{scheme := "http", path := Path} ->
|
||||||
|
ssl_manager:insert_crls(string:trim(Path, leading, "/"), CRLs);
|
||||||
|
_ ->
|
||||||
|
{error, {only_http_distribution_points_supported, URI}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_crls([], _) ->
|
||||||
|
not_available;
|
||||||
|
get_crls(
|
||||||
|
[{uniformResourceIdentifier, "http" ++ _ = URL} | Rest],
|
||||||
|
CRLDbInfo
|
||||||
|
) ->
|
||||||
|
case cache_lookup(URL, CRLDbInfo) of
|
||||||
|
[] ->
|
||||||
|
handle_http(URL, Rest, CRLDbInfo);
|
||||||
|
CRLs ->
|
||||||
|
CRLs
|
||||||
|
end;
|
||||||
|
get_crls([_ | Rest], CRLDbInfo) ->
|
||||||
|
%% unsupported CRL location
|
||||||
|
get_crls(Rest, CRLDbInfo).
|
||||||
|
|
||||||
|
http_lookup(URL, Rest, CRLDbInfo, Timeout) ->
|
||||||
|
case application:ensure_started(inets) of
|
||||||
|
ok ->
|
||||||
|
http_get(URL, Rest, CRLDbInfo, Timeout);
|
||||||
|
_ ->
|
||||||
|
get_crls(Rest, CRLDbInfo)
|
||||||
|
end.
|
||||||
|
|
||||||
|
http_get(URL, Rest, CRLDbInfo, Timeout) ->
|
||||||
|
case emqx_crl_cache:http_get(URL, Timeout) of
|
||||||
|
{ok, {_Status, _Headers, Body}} ->
|
||||||
|
case Body of
|
||||||
|
<<"-----BEGIN", _/binary>> ->
|
||||||
|
Pem = public_key:pem_decode(Body),
|
||||||
|
CRLs = lists:filtermap(
|
||||||
|
fun
|
||||||
|
({'CertificateList', CRL, not_encrypted}) ->
|
||||||
|
{true, CRL};
|
||||||
|
(_) ->
|
||||||
|
false
|
||||||
|
end,
|
||||||
|
Pem
|
||||||
|
),
|
||||||
|
emqx_crl_cache:register_der_crls(URL, CRLs),
|
||||||
|
CRLs;
|
||||||
|
_ ->
|
||||||
|
try public_key:der_decode('CertificateList', Body) of
|
||||||
|
_ ->
|
||||||
|
CRLs = [Body],
|
||||||
|
emqx_crl_cache:register_der_crls(URL, CRLs),
|
||||||
|
CRLs
|
||||||
|
catch
|
||||||
|
_:_ ->
|
||||||
|
get_crls(Rest, CRLDbInfo)
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
{error, _Reason} ->
|
||||||
|
get_crls(Rest, CRLDbInfo)
|
||||||
|
end.
|
||||||
|
|
||||||
|
cache_lookup(_, undefined) ->
|
||||||
|
[];
|
||||||
|
cache_lookup(URL, {{Cache, _}, _}) ->
|
||||||
|
#{path := Path} = uri_string:normalize(URL, [return_map]),
|
||||||
|
case ssl_pkix_db:lookup(string:trim(Path, leading, "/"), Cache) of
|
||||||
|
undefined ->
|
||||||
|
[];
|
||||||
|
[CRLs] ->
|
||||||
|
CRLs
|
||||||
|
end.
|
||||||
|
|
||||||
|
handle_http(URI, Rest, {_, [{http, Timeout}]} = CRLDbInfo) ->
|
||||||
|
CRLs = http_lookup(URI, Rest, CRLDbInfo, Timeout),
|
||||||
|
%% Uncomment to improve performance, but need to
|
||||||
|
%% implement cache limit and or cleaning to prevent
|
||||||
|
%% DoS attack possibilities
|
||||||
|
%%insert(URI, {der, CRLs}),
|
||||||
|
CRLs;
|
||||||
|
handle_http(_, Rest, CRLDbInfo) ->
|
||||||
|
get_crls(Rest, CRLDbInfo).
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
-module(emqx_common_test_helpers).
|
-module(emqx_common_test_helpers).
|
||||||
|
|
||||||
-include("emqx_authentication.hrl").
|
-include_lib("emqx/include/emqx_authentication.hrl").
|
||||||
|
|
||||||
-type special_config_handler() :: fun().
|
-type special_config_handler() :: fun().
|
||||||
|
|
||||||
|
@ -202,7 +202,6 @@ start_apps(Apps, SpecAppConfig, Opts) when is_function(SpecAppConfig) ->
|
||||||
%% Because, minirest, ekka etc.. application will scan these modules
|
%% Because, minirest, ekka etc.. application will scan these modules
|
||||||
lists:foreach(fun load/1, [emqx | Apps]),
|
lists:foreach(fun load/1, [emqx | Apps]),
|
||||||
ok = start_ekka(),
|
ok = start_ekka(),
|
||||||
mnesia:clear_table(emqx_admin),
|
|
||||||
ok = emqx_ratelimiter_SUITE:load_conf(),
|
ok = emqx_ratelimiter_SUITE:load_conf(),
|
||||||
lists:foreach(fun(App) -> start_app(App, SpecAppConfig, Opts) end, [emqx | Apps]).
|
lists:foreach(fun(App) -> start_app(App, SpecAppConfig, Opts) end, [emqx | Apps]).
|
||||||
|
|
||||||
|
@ -262,12 +261,13 @@ app_schema(App) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
mustache_vars(App, Opts) ->
|
mustache_vars(App, Opts) ->
|
||||||
ExtraMustacheVars = maps:get(extra_mustache_vars, Opts, []),
|
ExtraMustacheVars = maps:get(extra_mustache_vars, Opts, #{}),
|
||||||
[
|
Defaults = #{
|
||||||
{platform_data_dir, app_path(App, "data")},
|
platform_data_dir => app_path(App, "data"),
|
||||||
{platform_etc_dir, app_path(App, "etc")},
|
platform_etc_dir => app_path(App, "etc"),
|
||||||
{platform_log_dir, app_path(App, "log")}
|
platform_log_dir => app_path(App, "log")
|
||||||
] ++ ExtraMustacheVars.
|
},
|
||||||
|
maps:merge(Defaults, ExtraMustacheVars).
|
||||||
|
|
||||||
render_config_file(ConfigFile, Vars0) ->
|
render_config_file(ConfigFile, Vars0) ->
|
||||||
Temp =
|
Temp =
|
||||||
|
@ -275,7 +275,7 @@ render_config_file(ConfigFile, Vars0) ->
|
||||||
{ok, T} -> T;
|
{ok, T} -> T;
|
||||||
{error, Reason} -> error({failed_to_read_config_template, ConfigFile, Reason})
|
{error, Reason} -> error({failed_to_read_config_template, ConfigFile, Reason})
|
||||||
end,
|
end,
|
||||||
Vars = [{atom_to_list(N), iolist_to_binary(V)} || {N, V} <- Vars0],
|
Vars = [{atom_to_list(N), iolist_to_binary(V)} || {N, V} <- maps:to_list(Vars0)],
|
||||||
Targ = bbmustache:render(Temp, Vars),
|
Targ = bbmustache:render(Temp, Vars),
|
||||||
NewName = ConfigFile ++ ".rendered",
|
NewName = ConfigFile ++ ".rendered",
|
||||||
ok = file:write_file(NewName, Targ),
|
ok = file:write_file(NewName, Targ),
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,68 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIF+zCCA+OgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwbzELMAkGA1UEBhMCU0Ux
|
||||||
|
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQK
|
||||||
|
DAlNeU9yZ05hbWUxETAPBgNVBAsMCE15Um9vdENBMREwDwYDVQQDDAhNeVJvb3RD
|
||||||
|
QTAeFw0yMzAxMTIxMzA4MTZaFw0zMzAxMDkxMzA4MTZaMGsxCzAJBgNVBAYTAlNF
|
||||||
|
MRIwEAYDVQQIDAlTdG9ja2hvbG0xEjAQBgNVBAoMCU15T3JnTmFtZTEZMBcGA1UE
|
||||||
|
CwwQTXlJbnRlcm1lZGlhdGVDQTEZMBcGA1UEAwwQTXlJbnRlcm1lZGlhdGVDQTCC
|
||||||
|
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALQG7dMeU/y9HDNHzhydR0bm
|
||||||
|
wN9UGplqJOJPwqJRaZZcrn9umgJ9SU2il2ceEVxMDwzBWCRKJO5/H9A9k13SqsXM
|
||||||
|
2c2c9xXfIF1kb820lCm1Uow5hZ/auDjxliNk9kNJDigCRi3QoIs/dVeWzFsgEC2l
|
||||||
|
gxRqauN2eNFb6/yXY788YALHBsCRV2NFOFXxtPsvLXpD9Q/8EqYsSMuLARRdHVNU
|
||||||
|
ryaEF5lhShpcuz0TlIuTy2TiuXJUtJ+p7a4Z7friZ6JsrmQWsVQBj44F8TJRHWzW
|
||||||
|
C7vm9c+dzEX9eqbr5iPL+L4ctMW9Lz6ePcYfIXne6CElusRUf8G+xM1uwovF9bpV
|
||||||
|
+9IqY7tAu9G1iY9iNtJgNNDKOCcOGKcZCx6Cg1XYOEKReNnUMazvYeqRrrjV5WQ0
|
||||||
|
vOcD5zcBRNTXCddCLa7U0guXP9mQrfuk4NTH1Bt77JieTJ8cfDXHwtaKf6aGbmZP
|
||||||
|
wl1Xi/GuXNUP/xeog78RKyFwBmjt2JKwvWzMpfmH4mEkG9moh2alva+aEz6LIJuP
|
||||||
|
16g6s0Q6c793/OvUtpNcewHw4Vjn39LD9o6VLp854G4n8dVpUWSbWS+sXD1ZE69H
|
||||||
|
g/sMNMyq+09ufkbewY8xoCm/rQ1pqDZAVMWsstJEaYu7b/eb7R+RGOj1YECCV/Yp
|
||||||
|
EZPdDotbSNRkIi2d/a1NAgMBAAGjgaQwgaEwHQYDVR0OBBYEFExwhjsVUom6tQ+S
|
||||||
|
qq6xMUETvnPzMB8GA1UdIwQYMBaAFD90kfU5pc5l48THu0Ayj9SNpHuhMBIGA1Ud
|
||||||
|
EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMDsGA1UdHwQ0MDIwMKAuoCyG
|
||||||
|
Kmh0dHA6Ly9sb2NhbGhvc3Q6OTg3OC9pbnRlcm1lZGlhdGUuY3JsLnBlbTANBgkq
|
||||||
|
hkiG9w0BAQsFAAOCAgEAK6NgdWQYtPNKQNBGjsgtgqTRh+k30iqSO6Y3yE1KGABO
|
||||||
|
EuQdVqkC2qUIbCB0M0qoV0ab50KNLfU6cbshggW4LDpcMpoQpI05fukNh1jm3ZuZ
|
||||||
|
0xsB7vlmlsv00tpqmfIl/zykPDynHKOmFh/hJP/KetMy4+wDv4/+xP31UdEj5XvG
|
||||||
|
HvMtuqOS23A+H6WPU7ol7KzKBnU2zz/xekvPbUD3JqV+ynP5bgbIZHAndd0o9T8e
|
||||||
|
NFX23Us4cTenU2/ZlOq694bRzGaK+n3Ksz995Nbtzv5fbUgqmf7Mcq4iHGRVtV11
|
||||||
|
MRyBrsXZp2vbF63c4hrf2Zd6SWRoaDKRhP2DMhajpH9zZASSTlfejg/ZRO2s+Clh
|
||||||
|
YrSTkeMAdnRt6i/q4QRcOTCfsX75RFM5v67njvTXsSaSTnAwaPi78tRtf+WSh0EP
|
||||||
|
VVPzy++BszBVlJ1VAf7soWZHCjZxZ8ZPqVTy5okoHwWQ09WmYe8GfulDh1oj0wbK
|
||||||
|
3FjN7bODWHJN+bFf5aQfK+tumYKoPG8RXL6QxpEzjFWjxhIMJHHMKfDWnAV1o1+7
|
||||||
|
/1/aDzq7MzEYBbrgQR7oE5ZHtyqhCf9LUgw0Kr7/8QWuNAdeDCJzjXRROU0hJczp
|
||||||
|
dOyfRlLbHmLLmGOnROlx6LsGNQ17zuz6SPi7ei8/ylhykawDOAGkM1+xFakmQhM=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFzzCCA7egAwIBAgIUYjc7hD7/UJ0/VPADfNfp/WpOwRowDQYJKoZIhvcNAQEL
|
||||||
|
BQAwbzELMAkGA1UEBhMCU0UxEjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJ
|
||||||
|
U3RvY2tob2xtMRIwEAYDVQQKDAlNeU9yZ05hbWUxETAPBgNVBAsMCE15Um9vdENB
|
||||||
|
MREwDwYDVQQDDAhNeVJvb3RDQTAeFw0yMzAxMTIxMzA4MTRaFw00MzAxMDcxMzA4
|
||||||
|
MTRaMG8xCzAJBgNVBAYTAlNFMRIwEAYDVQQIDAlTdG9ja2hvbG0xEjAQBgNVBAcM
|
||||||
|
CVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMREwDwYDVQQLDAhNeVJvb3RD
|
||||||
|
QTERMA8GA1UEAwwITXlSb290Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
||||||
|
AoICAQCnBwSOYVJw47IoMHMXTVDtOYvUt3rqsurEhFcB4O8xmf2mmwr6m7s8A5Ft
|
||||||
|
AvAehg1GvnXT3t/KiyU7BK+acTwcErGyZwS2wvdB0lpHWSpOn/u5y+4ZETvQefcj
|
||||||
|
ZTdDOM9VN5nutpitgNb+1yL8sqSexfVbY7DnYYvFjOVBYoP/SGvM9jVjCad+0WL3
|
||||||
|
FhuD+L8QAxzCieX3n9UMymlFwINQuEc+TDjuNcEqt+0J5EgS1fwzxb2RCVL0TNv4
|
||||||
|
9a71hFGCNRj20AeZm99hbdufm7+0AFO7ocV5q43rLrWFUoBzqKPYIjga/cv/UdWZ
|
||||||
|
c5RLRXw3JDSrCqkf/mOlaEhNPlmWRF9MSus5Da3wuwgGCaVzmrf30rWR5aHHcscG
|
||||||
|
e+AOgJ4HayvBUQeb6ZlRXc0YlACiLToMKxuyxDyUcDfVEXpUIsDILF8dkiVQxEU3
|
||||||
|
j9g6qjXiqPVdNiwpqXfBKObj8vNCzORnoHYs8cCgib3RgDVWeqkDmlSwlZE7CvQh
|
||||||
|
U4Loj4l7813xxzYEKkVaT1JdXPWu42CG/b4Y/+f4V+3rkJkYzUwndX6kZNksIBai
|
||||||
|
phmtvKt+CTdP1eAbT+C9AWWF3PT31+BIhuT0u9tR8BVSkXdQB8dG4M/AAJcTo640
|
||||||
|
0mdYYOXT153gEKHJuUBm750ZTy+r6NjNvpw8VrMAakJwHqnIdQIDAQABo2MwYTAd
|
||||||
|
BgNVHQ4EFgQUP3SR9TmlzmXjxMe7QDKP1I2ke6EwHwYDVR0jBBgwFoAUP3SR9Tml
|
||||||
|
zmXjxMe7QDKP1I2ke6EwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw
|
||||||
|
DQYJKoZIhvcNAQELBQADggIBAFMFv4C+I0+xOAb9v6G/IOpfPBZ1ez31EXKJJBra
|
||||||
|
lulP4nRHQMeb310JS8BIeQ3dl+7+PkSxPABZSwc3jkxdSMvhc+Z4MQtTgos+Qsjs
|
||||||
|
gH7sTqwWeeQ0lHYxWmkXijrh5OPRZwTKzYQlkcn85BCUXl2KDuNEdiqPbDTao+lc
|
||||||
|
lA0/UAvC6NCyFKq/jqf4CmW5Kx6yG1v1LaE+IXn7cbIXj+DaehocVXi0wsXqj03Q
|
||||||
|
DDUHuLHZP+LBsg4e91/0Jy2ekNRTYJifSqr+9ufHl0ZX1pFDZyf396IgZ5CQZ0PJ
|
||||||
|
nRxZHlCfsxWxmxxdy3FQSE6YwXhdTjjoAa1ApZcKkkt1beJa6/oRLze/ux5x+5q+
|
||||||
|
4QczufHd6rjoKBi6BM3FgFQ8As5iNohHXlMHd/xITo1Go3CWw2j9TGH5vzksOElK
|
||||||
|
B0mcwwt2zwNEjvfytc+tI5jcfGN3tiT5fVHS8hw9dWKevypLL+55Ua9G8ZgDHasT
|
||||||
|
XFRJHgmnbyFcaAe26D2dSKmhC9u2mHBH+MaI8dj3e7wNBfpxNgp41aFIk+QTmiFW
|
||||||
|
VXFED6DHQ/Mxq93ACalHdYg18PlIYClbT6Pf2xXBnn33YPhn5xzoTZ+cDH/RpaQp
|
||||||
|
s0UUTSJT1UTXgtXPnZWQfvKlMjJEIiVFiLEC0sgZRlWuZDRAY0CdZJJxvQp59lqu
|
||||||
|
cbTm
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,32 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFdTCCA12gAwIBAgICEAUwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0Ux
|
||||||
|
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQL
|
||||||
|
DBBNeUludGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4X
|
||||||
|
DTIzMDExODEyMzY1NloXDTMzMDQyNTEyMzY1NlowgYQxCzAJBgNVBAYTAlNFMRIw
|
||||||
|
EAYDVQQIDAlTdG9ja2hvbG0xEjAQBgNVBAcMCVN0b2NraG9sbTESMBAGA1UECgwJ
|
||||||
|
TXlPcmdOYW1lMRkwFwYDVQQLDBBNeUludGVybWVkaWF0ZUNBMR4wHAYDVQQDDBVj
|
||||||
|
bGllbnQtbm8tZGlzdC1wb2ludHMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||||
|
AoIBAQCYQqNF7o20tEwyXphDgtwkZ628baYzQoCmmaufR+5SPQWdTN+GFeApv0dP
|
||||||
|
4y/ncZV24rgButMo73e4+wPsILwSGhaVIU0mMaCmexyC4W6INBkQsVB5FAd/YM0O
|
||||||
|
gdxS6A42h9HZTaAJ+4ftgFdOOHiP3lwicXeIYykAE7Y5ikxlnHgi8p1PTLowN4Q+
|
||||||
|
AjuXChRzmU16cUEAevZKkTVf7VCcK66aJsxBsxfykkGHhc6qLqmlMt6Te6DPCi/R
|
||||||
|
KP/kARTDWNEkp6qtpvzByYFYAKPSZxPuryajAC3RLuGNkVSB+PZ6NnZW6ASeTdra
|
||||||
|
Lwuiwsi5XPBeFb0147naQOBzSGG/AgMBAAGjggEHMIIBAzAJBgNVHRMEAjAAMBEG
|
||||||
|
CWCGSAGG+EIBAQQEAwIFoDBBBglghkgBhvhCAQ0ENBYyT3BlblNTTCBHZW5lcmF0
|
||||||
|
ZWQgQ2xpZW50IENlcnRpZmljYXRlIChubyBDUkwgaW5mbykwHQYDVR0OBBYEFBiV
|
||||||
|
sjDe46MixvftT/wej1mxGuN7MB8GA1UdIwQYMBaAFExwhjsVUom6tQ+Sqq6xMUET
|
||||||
|
vnPzMA4GA1UdDwEB/wQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
|
||||||
|
AwQwMQYIKwYBBQUHAQEEJTAjMCEGCCsGAQUFBzABhhVodHRwOi8vbG9jYWxob3N0
|
||||||
|
Ojk4NzcwDQYJKoZIhvcNAQELBQADggIBAKBEnKYVLFtZb3MI0oMJkrWBssVCq5ja
|
||||||
|
OYomZ61I13QLEeyPevTSWAcWFQ4zQDF/SWBsXjsrC+JIEjx2xac6XCpxcx3jDUgo
|
||||||
|
46u/hx2rT8tMKa60hW0V1Dk6w8ZHiCe94BlFLsWFKnn6dVzoJd2u3vgUaleh3uxF
|
||||||
|
hug8XY+wmHd36rO0kVe3DrsqdIdOfhMiJLDxU0cBA79vI5kCvqB8DIwCWtOzkA82
|
||||||
|
EPl3Iws5NPhuFAR9u0xOQu0akzmSJFcEGLZ4qfatHD/tZGRduyFvMKy5iIeMzuEs
|
||||||
|
2etm01tfLHqgKGOKp5LjPm7Aoac/GeVoTvctGF+wayvOuYE7inlGZToz3kQMMzHZ
|
||||||
|
ZGBBgOhXbR2y74QoFv6DUqmmTRbGfiLYyErA5r881ntgciQi02xrGjoAFntvKb+H
|
||||||
|
HNB22Qprz16OmdC9dJKF2RhO6Cketdhv65wFWw6xlhRMCWYPY3CI8tWkxS4A4yit
|
||||||
|
RZQZg3yaeHXMaCAu5HxuqAQXKGjz+7w7N6diwbT7o7CfKk8iHUrGfkQ5nCS0GZ1r
|
||||||
|
lU1vgKtdzVvJ6HmBrCRcdNqh/L/wdIltwI/52j+TKRtELM1qHuLAYmhcRBW+2wuH
|
||||||
|
ewaNA9KEgEk6JC+iR8uOBi0ZLkMIm47j+ZLJRJVUfgkVEEFjyiYSFfpwwcgT+/Aw
|
||||||
|
EczVZOdUEbDM
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCYQqNF7o20tEwy
|
||||||
|
XphDgtwkZ628baYzQoCmmaufR+5SPQWdTN+GFeApv0dP4y/ncZV24rgButMo73e4
|
||||||
|
+wPsILwSGhaVIU0mMaCmexyC4W6INBkQsVB5FAd/YM0OgdxS6A42h9HZTaAJ+4ft
|
||||||
|
gFdOOHiP3lwicXeIYykAE7Y5ikxlnHgi8p1PTLowN4Q+AjuXChRzmU16cUEAevZK
|
||||||
|
kTVf7VCcK66aJsxBsxfykkGHhc6qLqmlMt6Te6DPCi/RKP/kARTDWNEkp6qtpvzB
|
||||||
|
yYFYAKPSZxPuryajAC3RLuGNkVSB+PZ6NnZW6ASeTdraLwuiwsi5XPBeFb0147na
|
||||||
|
QOBzSGG/AgMBAAECggEACSMuozq+vFJ5pCgzIRIQXgruzTkTWU4rZFQijYuGjN7m
|
||||||
|
oFsFqwlTC45UHEI5FL2nR5wxiMEKfRFp8Or3gEsyni98nXSDKcCesH8A5gXbWUcv
|
||||||
|
HeZWOv3tuUI47B709vDAMZuTB2R2L0MuFB24n5QaACBLDTIcB05UHpIQRIG9NffH
|
||||||
|
MhxqFB2kuakp67VekYGZkBCNkqfL3VQZIGRpQC8SvpnRXELqZgI4MyJgvkK6myWj
|
||||||
|
Vtpwm8YiOQoJHJx4raoVfS2NWTsCwL0M0aXMMtmM2QfMP/xB9OifxnmDDBs7Tie8
|
||||||
|
0Wri845xLTCYthaU8B06rhoQdKXoqKmQMoF2doPm8QKBgQDN+0E0PtPkyxIho8pV
|
||||||
|
CsQnmif91EQQqWxOdkHbE96lT0UKu6ziBSbB4ClRHYil5c8p7INxRpj7pruOY3Kw
|
||||||
|
MAcacIMMBNhLBJL4R0hr/pwr18WOZxCIMcLHTaCfbVqL71TKp4/6C+GexZfaYJ46
|
||||||
|
IZEpLU5RPmD4f9MPIDDm6KcPxwKBgQC9O9TOor93g+A4sU54CGOqvVDrdi5TnGF8
|
||||||
|
YdimvUsT20gl2WGX5vq3OohzZi7U8FuxKHWpbgh2efqGLcFsRNFZ/T0ZXX4DDafN
|
||||||
|
Gzyu/DMVuFO4ccgFJNnl45w3/yFG40kL6yS8kss/iEYu550/uOZ1FjH+kJ0vjV6G
|
||||||
|
JD8q0PgOSQKBgG2i9cLcSia2nBEBwFlhoKS/ndeyWwRPWZGtykHUoqZ0ufgLiurG
|
||||||
|
+SkqqnM9eBVta8YR2Ki7fgQ8bApPDqWO+sjs6CPGlGXhqmSydG7fF7sSX1n7q8YC
|
||||||
|
Tn2M6RjSuOZQ3l37sFvUZSQAYmJfGPkyErTLI6uEu1KpnuqnJMBTR1DTAoGAIGQn
|
||||||
|
bx9oirqmHM4s0lsNRGKXgVZ/Y4x3G2VcQl5QhZuZY/ErxWaiL87zIF2zUnu6Fj8I
|
||||||
|
tPHCvRTwDxux6ih1dWPlm3vnX/psaK1q28ELtYIRwpanWEoQiktFqEghmBK7pDCh
|
||||||
|
3y15YOygptK6lfe+avhboml6nnMiZO+7aEbQzxECgYALuUM4fo1dQYmYuZIqZoFJ
|
||||||
|
TXGyzMkNGs61SMiD6mW6XgXj5h5T8Q0MdpmHkwsm+z9A/1of5cxkE6d8HCCz+dt5
|
||||||
|
tnY7OC0gYB1+gDld8MZgFgP6k0qklreLVhzEz11TbMldifa1EE4VjUDG/NeAEtbq
|
||||||
|
GbLaw0NhGJtRCgL9Bc7i7g==
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,32 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFnDCCA4SgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0Ux
|
||||||
|
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQL
|
||||||
|
DBBNeUludGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4X
|
||||||
|
DTIzMDExMjEzMDgxNloXDTMzMDQxOTEzMDgxNlowfTELMAkGA1UEBhMCU0UxEjAQ
|
||||||
|
BgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQKDAlN
|
||||||
|
eU9yZ05hbWUxGTAXBgNVBAsMEE15SW50ZXJtZWRpYXRlQ0ExFzAVBgNVBAMMDmNs
|
||||||
|
aWVudC1yZXZva2VkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs+R6
|
||||||
|
PDtIxVlUoLYbDBbaVcxgoLjnWcvqL8wSqyWuqi/Y3cjuNYCziR9nR5dWajtkBjzJ
|
||||||
|
HyhgAr6gBVSRt4RRmDXoOcprK3GcpowAr65UAmC4hdH0af6FdKjKCnFw67byUg52
|
||||||
|
f7ueXZ6t/XuuKxlU/f2rjXVwmmnlhBi5EHDkXxvfgWXJekDfsPbW9j0kaCUWCpfj
|
||||||
|
rzGbfkXqrPkslO41PYlCbPxoiRItJjindFjcQySYvRq7A2uYMGsrxv4n3rzo5NGt
|
||||||
|
goBmnGj61ii9WOdopcFxKirhIB9zrxC4x0opRfIaF/n1ZXk6NOnaDxu1LTZ18wfC
|
||||||
|
ZB979ge6pleeKoPf7QIDAQABo4IBNjCCATIwCQYDVR0TBAIwADARBglghkgBhvhC
|
||||||
|
AQEEBAMCBaAwMwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIENsaWVu
|
||||||
|
dCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUQeItXr3nc6CZ++G9UCoq1YlQ9oowHwYD
|
||||||
|
VR0jBBgwFoAUTHCGOxVSibq1D5KqrrExQRO+c/MwDgYDVR0PAQH/BAQDAgXgMB0G
|
||||||
|
A1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDA7BgNVHR8ENDAyMDCgLqAshipo
|
||||||
|
dHRwOi8vbG9jYWxob3N0Ojk4NzgvaW50ZXJtZWRpYXRlLmNybC5wZW0wMQYIKwYB
|
||||||
|
BQUHAQEEJTAjMCEGCCsGAQUFBzABhhVodHRwOi8vbG9jYWxob3N0Ojk4NzcwDQYJ
|
||||||
|
KoZIhvcNAQELBQADggIBAIFuhokODd54/1B2JiNyG6FMq/2z8B+UquC2iw3p2pyM
|
||||||
|
g/Jz4Ouvg6gGwUwmykEua06FRCxx5vJ5ahdhXvKst/zH/0qmYTFNMhNsDy76J/Ot
|
||||||
|
Ss+VwQ8ddpEG3EIUI9BQxB3xL7z7kRQzploQjakNcDWtDt1BmN05Iy2vz4lnYJky
|
||||||
|
Kss6ya9jEkNibHekhxJuchJ0fVGlVe74MO7RNDFG7+O3tMlxu0zH/LpW093V7BI2
|
||||||
|
snXNAwQBizvWTrDKWLDu5JsX8KKkrmDtFTs9gegnxDCOYdtG5GbbMq+H1SjWUJPV
|
||||||
|
wiXTF8/eE02s4Jzm7ZAxre4bRt/hAg7xTGmDQ1Hn+LzLn18I9LaW5ZWqSwwpgv+g
|
||||||
|
Z/jiLO9DJ/y525Cl7DLCpSFoDTWlQXouKhcgALcVay/cXCsZ3oFZCustburLiJi/
|
||||||
|
zgBeEk1gVpwljriJLeZifyfWtJx6yfgB/h6fid8XLsGRD+Yc8Tzs8J1LIgi+j4ZT
|
||||||
|
UzKX3B85Kht/dr43UDMtWOF3edkOMaJu7rcg5tTsK+LIyHtXvebKPVvvA9f27Dz/
|
||||||
|
4gmhAwwqS87Xv3FMVhZ03DNOJ6XAF+T6OTEqwYs+iK56IMSl1Jy+bCzo0j5jZVbl
|
||||||
|
XFwGxUHzM7pfM6PDx657oUxG1QwM/fIWA18F+kY/yigXxq6pYMeAiQsPanOThgHp
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCz5Ho8O0jFWVSg
|
||||||
|
thsMFtpVzGCguOdZy+ovzBKrJa6qL9jdyO41gLOJH2dHl1ZqO2QGPMkfKGACvqAF
|
||||||
|
VJG3hFGYNeg5ymsrcZymjACvrlQCYLiF0fRp/oV0qMoKcXDrtvJSDnZ/u55dnq39
|
||||||
|
e64rGVT9/auNdXCaaeWEGLkQcORfG9+BZcl6QN+w9tb2PSRoJRYKl+OvMZt+Reqs
|
||||||
|
+SyU7jU9iUJs/GiJEi0mOKd0WNxDJJi9GrsDa5gwayvG/ifevOjk0a2CgGacaPrW
|
||||||
|
KL1Y52ilwXEqKuEgH3OvELjHSilF8hoX+fVleTo06doPG7UtNnXzB8JkH3v2B7qm
|
||||||
|
V54qg9/tAgMBAAECggEAAml+HRgjZ+gEezot3yngSBW7NvR7v6e9DmKDXpGdB7Go
|
||||||
|
DANBdGyzG5PU9/AGy9pbgzzl6nnJXcgOD7w8TvRifrK8WCgHa1f05IPMj458GGMR
|
||||||
|
HlQ8HX647eFEgkLWo4Z6tdB1VM2geDtkNFmn8nJ+wgAYgIdSWPOyDOUi+B43ZbIN
|
||||||
|
eaLWkP2fiX9tcJp41cytW+ng2YIm4s90Nt4FJPNBNzOrhVm35jciId02MmEjCEnr
|
||||||
|
0YbK9uoMDC2YLg8vhRcjtsUHV2rREkwEAQj8nCWvWWheIwk943d6OicGAD/yebpV
|
||||||
|
PTjtlZlpIbrovfvuMcoTxJg3WS8LTg/+cNWAX5a3eQKBgQDcRY7nVSJusYyN0Bij
|
||||||
|
YWc9H47wU+YucaGT25xKe26w1pl6s4fmr1Sc3NcaN2iyUv4BuAvaQzymHe4g9deU
|
||||||
|
D9Ws/NCQ9EjHJJsklNyn2KCgkSp7oPKhPwyl64XfPdV2gr5AD6MILf7Rkyib5sSf
|
||||||
|
1WK8i25KatT7M4mCtrBVJYHNpQKBgQDREjwPIaQBPXouVpnHhSwRHfKD0B1a2koq
|
||||||
|
4VE6Fnf3ogkiGfV9kqXwIfPHL0tfotFraM3FFmld8RcxhKUPr4oj+K9KTxmMD9lm
|
||||||
|
9Hal0ANXYmHs5a1iHyoNmTpBGHALWLT9fCoeg+EIYabi2+P1c7cDIdUPkEzo4GmI
|
||||||
|
nCIpv7hGqQKBgEFUC+8GK+EinWoN1tDV+ZWCP5V9fJ43q1E7592bQBgIfZqLlnnP
|
||||||
|
dEvVn6Ix3sZMoPMHj9Ra7qjh5Zc28ooCLEBS9tSW7uLJM44k7FCHihQ1GaFy+aLj
|
||||||
|
HTA0aw7rutycKCq9uH+bjKDBgWDDj3tMAS2kOMCvcJ1UCquO3TtTlWzVAoGBAIDN
|
||||||
|
8yJ/X0NEVNnnkKZTbWq+QILk3LD0e20fk6Nt5Es0ENxpkczjZEglIsM8Z/trnAnI
|
||||||
|
b71UqWWu+tMPHYIka77tn1DwmpSnzxCW2+Ib3XMgsaP5fHBPMuFd3X3tSFo1NIxW
|
||||||
|
yrwyE5nOT7rELhUyTTYoydLk2/09BMedKY7/BtDBAoGAXeX1pX74K1i/uWyYKwYZ
|
||||||
|
sskRueSo9whDJuZWgNiUovArr57eA+oA+bKdFpiE419348bkFF8jNoGFQ6MXMedD
|
||||||
|
LqHAYIj+ZPIC4+rObHqO5EaIyblgutwx3citkQp7HXDBxojnOKA9mKQXj1vxCaL1
|
||||||
|
/1fFNJQCzEqwnKwnhI2MJ28=
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,32 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFljCCA36gAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0Ux
|
||||||
|
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQL
|
||||||
|
DBBNeUludGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4X
|
||||||
|
DTIzMDExMjEzMDgxNloXDTMzMDQxOTEzMDgxNlowdzELMAkGA1UEBhMCU0UxEjAQ
|
||||||
|
BgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQKDAlN
|
||||||
|
eU9yZ05hbWUxGTAXBgNVBAsMEE15SW50ZXJtZWRpYXRlQ0ExETAPBgNVBAMMCE15
|
||||||
|
Q2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvGuAShewEo8V
|
||||||
|
/+aWVO/MuUt92m8K0Ut4nC2gOvpjMjf8mhSSf6KfnxPklsFwP4fdyPOjOiXwCsf3
|
||||||
|
1QO5fjVr8to3iGTHhEyZpzRcRqmw1eYJC7iDh3BqtYLAT30R+Kq6Mk+f4tXB5Lp/
|
||||||
|
2jXgdi0wshWagCPgJO3CtiwGyE8XSa+Q6EBYwzgh3NFbgYdJma4x+S86Y/5WfmXP
|
||||||
|
zF//UipsFp4gFUqwGuj6kJrN9NnA1xCiuOxCyN4JuFNMfM/tkeh26jAp0OHhJGsT
|
||||||
|
s3YiUm9Dpt7Rs7o0so9ov9K+hgDFuQw9HZW3WIJI99M5a9QZ4ZEQqKpABtYBl/Nb
|
||||||
|
VPXcr+T3fQIDAQABo4IBNjCCATIwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMC
|
||||||
|
BaAwMwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIENsaWVudCBDZXJ0
|
||||||
|
aWZpY2F0ZTAdBgNVHQ4EFgQUOIChBA5aZB0dPWEtALfMIfSopIIwHwYDVR0jBBgw
|
||||||
|
FoAUTHCGOxVSibq1D5KqrrExQRO+c/MwDgYDVR0PAQH/BAQDAgXgMB0GA1UdJQQW
|
||||||
|
MBQGCCsGAQUFBwMCBggrBgEFBQcDBDA7BgNVHR8ENDAyMDCgLqAshipodHRwOi8v
|
||||||
|
bG9jYWxob3N0Ojk4NzgvaW50ZXJtZWRpYXRlLmNybC5wZW0wMQYIKwYBBQUHAQEE
|
||||||
|
JTAjMCEGCCsGAQUFBzABhhVodHRwOi8vbG9jYWxob3N0Ojk4NzcwDQYJKoZIhvcN
|
||||||
|
AQELBQADggIBAE0qTL5WIWcxRPU9oTrzJ+oxMTp1JZ7oQdS+ZekLkQ8mP7T6C/Ew
|
||||||
|
6YftjvkopnHUvn842+PTRXSoEtlFiTccmA60eMAai2tn5asxWBsLIRC9FH3LzOgV
|
||||||
|
/jgyY7HXuh8XyDBCDD+Sj9QityO+accTHijYAbHPAVBwmZU8nO5D/HsxLjRrCfQf
|
||||||
|
qf4OQpX3l1ryOi19lqoRXRGwcoZ95dqq3YgTMlLiEqmerQZSR6iSPELw3bcwnAV1
|
||||||
|
hoYYzeKps3xhwszCTz2+WaSsUO2sQlcFEsZ9oHex/02UiM4a8W6hGFJl5eojErxH
|
||||||
|
7MqaSyhwwyX6yt8c75RlNcUThv+4+TLkUTbTnWgC9sFjYfd5KSfAdIMp3jYzw3zw
|
||||||
|
XEMTX5FaLaOCAfUDttPzn+oNezWZ2UyFTQXQE2CazpRdJoDd04qVg9WLpQxLYRP7
|
||||||
|
xSFEHulOPccdAYF2C45yNtJAZyWKfGaAZIxrgEXbMkcdDMlYphpRwpjS8SIBNZ31
|
||||||
|
KFE8BczKrg2qO0ywIjanPaRgrFVmeSvBKeU/YLQVx6fZMgOk6vtidLGZLyDXy0Ff
|
||||||
|
yaZSoj+on++RDz1IXb96Y8scuNlfcYI8QeoNjwiLtf80BV8SRJiG4e/jTvMf/z9L
|
||||||
|
kWrnDWvx4xkUmxFg4TK42dkNp7sEYBTlVVq9fjKE92ha7FGZRqsxOLNQ
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC8a4BKF7ASjxX/
|
||||||
|
5pZU78y5S33abwrRS3icLaA6+mMyN/yaFJJ/op+fE+SWwXA/h93I86M6JfAKx/fV
|
||||||
|
A7l+NWvy2jeIZMeETJmnNFxGqbDV5gkLuIOHcGq1gsBPfRH4qroyT5/i1cHkun/a
|
||||||
|
NeB2LTCyFZqAI+Ak7cK2LAbITxdJr5DoQFjDOCHc0VuBh0mZrjH5Lzpj/lZ+Zc/M
|
||||||
|
X/9SKmwWniAVSrAa6PqQms302cDXEKK47ELI3gm4U0x8z+2R6HbqMCnQ4eEkaxOz
|
||||||
|
diJSb0Om3tGzujSyj2i/0r6GAMW5DD0dlbdYgkj30zlr1BnhkRCoqkAG1gGX81tU
|
||||||
|
9dyv5Pd9AgMBAAECggEAAifx6dZKIeNkQ8OaNp5V2IKIPSqBOV4/h/xKMkUZXisV
|
||||||
|
eDmTCf8du0PR7hfLqrt9xYsGDv+6FQ1/8K231l8qR0tP/6CTl/0ynM4qqEAGeFXN
|
||||||
|
3h2LvM4liFbdjImechrcwcnVaNKg/DogT5zHUYSMtB/rokaG0VBO3IX/+SGz0aXi
|
||||||
|
LOLAx6SPaLOVX9GYUCiigTSEDwaQA+F3F6J2fR4u8PrXo+OQUqxjQ/fGXWp+4IfA
|
||||||
|
6djlpvzO2849/WPB1tL20iLXJlL2OL0UgQNtbKWTjexMe+wgCR5BzCwTyPsQvMwX
|
||||||
|
YOQrTOwgF3b6O+gLks5wSRT0ivq1sKgzA534+X4M+wKBgQDirPTLlrYobOO8KUpV
|
||||||
|
LOJU8x9leiRNU9CZWrW/mOw/BXGXikqNWvgL595vvADsjYciuRxSqEE7lClB8Pp9
|
||||||
|
20TMlES9orx7gdoQJCodpNV1BuBJhE9YtUiXzWAj+7m3D9LsXM1ewW/2A7Vvopj3
|
||||||
|
4zKY7uHAFlo3nXwLOfChG5/i9wKBgQDUy5fPFa58xmn7Elb6x4vmUDHg6P4pf75E
|
||||||
|
XHRQvNA8I7DTrpqfcsF1N4WuJ3Lm//RSpw7bnyqP20GoEfGHu/iCUPf29B7CuXhO
|
||||||
|
vvD+I8uPdn8EcKUBWV+V0xNQN/gCe0TzrEjAkZcO2Lq0j93R8HVl3BbowxgRvQV9
|
||||||
|
GmxQG/boKwKBgFeV8uSzsGEAaiKrZbBxrmaappgEUQCcES8gULfes/JJ/TFL2zCx
|
||||||
|
ZMTc7CMKZuUAbqXpFtuNbd9CiYqUPYXh8ryF0eXgeqnSa9ruzmMz7NLSPFnLyQkC
|
||||||
|
yzD0x2BABOuKLrrrxOMHJWbO2g1vq2GlJUjYjNw3BtcUf/iqg6MM1IPTAoGAWYWJ
|
||||||
|
SSqS7JVAcsrFYt1eIrdsNHVwr565OeM3X9v/Mr3FH1jeXeQWNSz1hU29Ticx7y+u
|
||||||
|
1YBBlKGmHoHl/bd7lb9ggjkzU7JZRa+YjSIb+i/cwc5t7IJf7xUMk/vnz4tyd5zs
|
||||||
|
Qm89gJZ2/Y1kwXSKvx53WNbyokvGKlpaZN1O418CgYACliGux77pe4bWeXSFFd9N
|
||||||
|
50ipxDLVghw1c5AiZn25GR5YHJZaV4R0wmFcHdZvogLKi0jDMPvU69PaiT8eX/A1
|
||||||
|
COkxv7jY1vtKlEtb+gugMjMN8wvb2va4kyFamjqnleiZlBSqIF/Y17wBoMvaWgZ0
|
||||||
|
bEPCN//ts5hBwgb1TwGrrg==
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,32 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFljCCA36gAwIBAgICEAowDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0Ux
|
||||||
|
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQL
|
||||||
|
DBBNeUludGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4X
|
||||||
|
DTIzMDMxNjIwMjAzMloXDTMzMDYyMTIwMjAzMlowdjELMAkGA1UEBhMCU0UxEjAQ
|
||||||
|
BgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQKDAlN
|
||||||
|
eU9yZ05hbWUxGTAXBgNVBAsMEE15SW50ZXJtZWRpYXRlQ0ExEDAOBgNVBAMMB0Ns
|
||||||
|
aWVudDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDcDhlEvUIYc9uA
|
||||||
|
ocOBXt5thKrovs+8V0Eus/WrHMTKBk0Kw4X+7HBaRBoZj2sZpYfN63lVaO75kW4I
|
||||||
|
uJuorGj5PAXYWJj+4uAsCc95xAN/liCuHJnxE5togWVt8W+z0Zll98RIpiCohqiE
|
||||||
|
FLDL4X6FREL07GLgQZ/BFORvAwU+Gog05AFh43iZDnJl8MmrG2HBSRXtSZ6vQj9A
|
||||||
|
NrOSqz5eK4YIHEEsgwTWQmhtNwu3Y+GzrAPWCA4TeYrSRwIrnGh20fOWXkAMldS4
|
||||||
|
eRXmBztEsXMGqbe6oYO1QPYOlmoGO8EaaDPJ2sFIuM0zn98Alq3kCnRhM5Bi9RpJ
|
||||||
|
7IpudIopAgMBAAGjggE3MIIBMzAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIF
|
||||||
|
oDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgQ2xpZW50IENlcnRp
|
||||||
|
ZmljYXRlMB0GA1UdDgQWBBQoIuXq3wG6JEzAEj9wPe7am0OVgjAfBgNVHSMEGDAW
|
||||||
|
gBRMcIY7FVKJurUPkqqusTFBE75z8zAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYw
|
||||||
|
FAYIKwYBBQUHAwIGCCsGAQUFBwMEMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9s
|
||||||
|
b2NhbGhvc3Q6OTg3OC9pbnRlcm1lZGlhdGUxLmNybC5wZW0wMQYIKwYBBQUHAQEE
|
||||||
|
JTAjMCEGCCsGAQUFBzABhhVodHRwOi8vbG9jYWxob3N0Ojk4NzcwDQYJKoZIhvcN
|
||||||
|
AQELBQADggIBAHqKYcwkm3ODPD7Mqxq3bsswSXregWfc8tqfIBc5FZg2F+IzhxcJ
|
||||||
|
kINB0lmcNdLALK6ka0sDs1Nrj1KB96NcHUqE+WY/qPS1Yksr34yFatb1ddlKQ9HK
|
||||||
|
VRrIsi0ZfjBpHpvoQ0GsLeyRKm7iN/Fm5H9u8rw6RBu0Oe/l20FVSQIDzldYw51L
|
||||||
|
uV/E9No8ZhdQ2Dffujs8madI7b7I1NMXS+Z1pZ+gYrz6O60tDEprE+rYuYWypURr
|
||||||
|
fK+DnLLl+KQ+eekTPynw7LRpFzI/1cOMmd4BRnsBHCbCObfNp7WPasemZOEXGIlZ
|
||||||
|
CQwZS62DYOJE4u4Nz5pSF+JgXfr6X/Im6Y1SV900xVHfoL0GpFDI9k+0Y5ncHfSH
|
||||||
|
+V9HlRWB3zqQF+yla32XOpBbER0vFDH52gp8/o1ZGg7rr6KrP4QKxnqywNLiAPDX
|
||||||
|
txaAykZhON7uG8j+Lbjx5Ik91NRn9Fd5NH/vtT33a4uig2TP9EWd7EPcD2z8ONuD
|
||||||
|
yiK3S37XAnmSKKX4HcCpEb+LedtqQo/+sqWyWXkpKdpkUSozvcYS4J/ob3z9N2IE
|
||||||
|
qIH5I+Mty1I4EB4W89Pem8DHNq86Lt0Ea6TBtPTV8NwR5aG2vvLzb5lNdpANXYcp
|
||||||
|
nGr57mTWaHnQh+yqgy66J++k+WokWkAkwE989AvUfNoQ+Jr6cTH8nKo2
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDcDhlEvUIYc9uA
|
||||||
|
ocOBXt5thKrovs+8V0Eus/WrHMTKBk0Kw4X+7HBaRBoZj2sZpYfN63lVaO75kW4I
|
||||||
|
uJuorGj5PAXYWJj+4uAsCc95xAN/liCuHJnxE5togWVt8W+z0Zll98RIpiCohqiE
|
||||||
|
FLDL4X6FREL07GLgQZ/BFORvAwU+Gog05AFh43iZDnJl8MmrG2HBSRXtSZ6vQj9A
|
||||||
|
NrOSqz5eK4YIHEEsgwTWQmhtNwu3Y+GzrAPWCA4TeYrSRwIrnGh20fOWXkAMldS4
|
||||||
|
eRXmBztEsXMGqbe6oYO1QPYOlmoGO8EaaDPJ2sFIuM0zn98Alq3kCnRhM5Bi9RpJ
|
||||||
|
7IpudIopAgMBAAECggEARcly2gnrXDXh9vlWN0EO6UyZpxZcay6AzX7k+k81WZyF
|
||||||
|
8lPvutjhCL9wR4rkPE3ys6tp31xX7W3hp4JkWynSYLhYYjQ20R7CWTUDR2qScXP7
|
||||||
|
CTyo1XuSXaIruKJI+o4OR/g7l46X7NpHtxuYtg/dQAZV9bbB5LzrHSCzEUGz9+17
|
||||||
|
jV//cBgLBiMdlbdLuQoGt4NQpBkNrauBVFq7Nq648uKkICmUo3Bzn/dfn3ehB+Zc
|
||||||
|
+580S+tawYd224j19tFQmd5oK8tfjqKuHenNGjp/gsRoY86N7qAtc3VIQ0yjE6ez
|
||||||
|
tgREo/ftCb8kGfwRJOAQIeeDamBv+FWNT6QzcOtbwQKBgQDzWhY9BUgI8JVzjYg0
|
||||||
|
oWfU90On81BtckKsEo//8MTlgwOD2PnUF0hTZF3RcSPYT+HybouTqHT8EOLBAzqy
|
||||||
|
1+koH06MnAc/Y2ipaAe2fGaVH3SuXAsV/b8VcWWl4Qx7tYJDhE7sKmdl3/+jHZ7A
|
||||||
|
hZQzgOQnxxCANBo3pwF9KboDbwKBgQDnfglSpgYdGzFpWp1hZnPl2RKIfX/4M2z2
|
||||||
|
s+hVN1tp+1VySPrBRUC3J6hKPQUzzeUzPICclHOnO+kP7jAos/rlJ9VcNdAQTbTL
|
||||||
|
7Ds9Em1KJTBphE038YbW3e93rydQpukXh50wRD9RI/3F3A/1rKhab92DXZZr6Wqu
|
||||||
|
JouhNV8f5wKBgQCLQ3XQi/Iyc4QDse5NuETUgoCsX7kaOTZghOr1nFMByV08mfI2
|
||||||
|
5vAUES8DigzqYKS8eXjVEqWIDx3FOVThPmCG/ouUOkKHixs9P3SSgVSvaGX81l3d
|
||||||
|
wu4UlmWGbWkYbsJSYyhLTOUJTwxby7qrEIbEhrGK9gfCZo7OZHucpkF2bwKBgFhl
|
||||||
|
1qWK5JbExY+XnLWO6/7/b4ZTdkSPTrK+bJ/t7aiA41Yq7CZVjarjJ+6BcrUfkMCK
|
||||||
|
AArK3Yck55C/wgApCkvrdBwsKHGxWrLsWIqvuLAxl1UTwnD0eCsgwMsRRZAUzLnB
|
||||||
|
fZLq3MrdVZDywd1suzUdtpbta/11OtmZuoQq31JNAoGAIzmevuPaUZRqnjDpLXAm
|
||||||
|
Bo11q6CunhG5qZX4wifeZ9Fp5AaQu97F36igZ5/eDxFkDCrFRq6teMiNjRQZSLA3
|
||||||
|
5tMBkq6BtN2Ozzm/6D135c4BF14ODHqAMPUy4sXbw5PS/kRFs4fKAH/+LcAOPgyI
|
||||||
|
N/jJIY1LfM7PzrG2NdyscMU=
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,32 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFljCCA36gAwIBAgICEAswDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0Ux
|
||||||
|
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQL
|
||||||
|
DBBNeUludGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4X
|
||||||
|
DTIzMDMxNjIwMjAzMloXDTMzMDYyMTIwMjAzMlowdjELMAkGA1UEBhMCU0UxEjAQ
|
||||||
|
BgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQKDAlN
|
||||||
|
eU9yZ05hbWUxGTAXBgNVBAsMEE15SW50ZXJtZWRpYXRlQ0ExEDAOBgNVBAMMB0Ns
|
||||||
|
aWVudDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDFLcCjzNhfY6Sk
|
||||||
|
2nSdrB/6UPPeTCCH5NBBVLvu1hdlqLS4qEdq8+EjyMDZTtspRtYPkBfjpOrlBWUO
|
||||||
|
lKyxw2mZOjZ8iWvd4sJaAI/6KZl5X0Rdsg1RjzW03kUdLx9XJCyrYY0YFrT1dgJo
|
||||||
|
Ih56jk2SJX7wrz0NCJ05VPIdpaOF6CcziA+YhdVHcE6xyHagsYI0JdDWxFZrl9zT
|
||||||
|
LyhaDgBUN/yUQBnxKzxs8TMT4YVSi73ouh5IP9Xvs52hd6HO8ZGVr+YthQZKo95p
|
||||||
|
OlwFF+AQWxdDIKoPYUPFo8XMOXvOeQ9iUJarxrYSrelLXtGkaGLBolAvqo/YKE7j
|
||||||
|
rcJWjRGHAgMBAAGjggE3MIIBMzAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIF
|
||||||
|
oDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgQ2xpZW50IENlcnRp
|
||||||
|
ZmljYXRlMB0GA1UdDgQWBBTOo9YSgx1h5k/imP7nOfRfzQrRxjAfBgNVHSMEGDAW
|
||||||
|
gBRMcIY7FVKJurUPkqqusTFBE75z8zAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYw
|
||||||
|
FAYIKwYBBQUHAwIGCCsGAQUFBwMEMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9s
|
||||||
|
b2NhbGhvc3Q6OTg3OC9pbnRlcm1lZGlhdGUyLmNybC5wZW0wMQYIKwYBBQUHAQEE
|
||||||
|
JTAjMCEGCCsGAQUFBzABhhVodHRwOi8vbG9jYWxob3N0Ojk4NzcwDQYJKoZIhvcN
|
||||||
|
AQELBQADggIBAFo91lLqjPY67Wmj2yWxZuTTuUwXdXXUQxL6sEUUnfkECvRhNyBA
|
||||||
|
eCHkfVopNbXZ5tdLfsUvXF0ulaC76GCK/P7gHOG9D/RJX/85VzhuJcqa4dsEEifg
|
||||||
|
IiKIG7viYxSA6HFXuyzHvwNco3FqTBHbY46lKf1lWRVLhiAtcwcyPP34/RWcPfQi
|
||||||
|
6NZfLyitu5U7Z9XVN5wCp8sg0ayaO5Ib2ejIYuBCUddV1gV//tSDf+rKCgtAbm/X
|
||||||
|
K64Bf3GdaX3h6EhoqMZ+Z2f4XpKSXTabsWAU44xdVxisI82eo+NwT8KleE65GpOv
|
||||||
|
nPvr/dLq5fQ6VtHbRL3wWqhzB1VKVCtd8a6RE2k8HVWflU3qgwJ+woF19ed921eq
|
||||||
|
OZxc+KzjsGFyW1D2fPdgoZFmePadSstIME7qtCNEi7D3im01/1KKzE2m/nosrHeW
|
||||||
|
ePjY2YrXu0w47re/N2kBJL2xRbj+fAjBsfNn9RhvQsWheXG6mgg8w1ac6y72ZA2W
|
||||||
|
72pWoDkgXQMX5XBBj/zMnmwtrX9zTILFjNGFuWMPYgBRI0xOf2FoqqZ67cQ2yTW/
|
||||||
|
1T/6Mp0FSh4cIo/ENiNSdvlt3BIo84EyOm3iHHy28Iv5SiFjF0pkwtXlYYvjM3+R
|
||||||
|
BeWqlPsVCZXcVC1rPVDzfWZE219yghldY4I3QPJ7dlmszi8eI0HtzhTK
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFLcCjzNhfY6Sk
|
||||||
|
2nSdrB/6UPPeTCCH5NBBVLvu1hdlqLS4qEdq8+EjyMDZTtspRtYPkBfjpOrlBWUO
|
||||||
|
lKyxw2mZOjZ8iWvd4sJaAI/6KZl5X0Rdsg1RjzW03kUdLx9XJCyrYY0YFrT1dgJo
|
||||||
|
Ih56jk2SJX7wrz0NCJ05VPIdpaOF6CcziA+YhdVHcE6xyHagsYI0JdDWxFZrl9zT
|
||||||
|
LyhaDgBUN/yUQBnxKzxs8TMT4YVSi73ouh5IP9Xvs52hd6HO8ZGVr+YthQZKo95p
|
||||||
|
OlwFF+AQWxdDIKoPYUPFo8XMOXvOeQ9iUJarxrYSrelLXtGkaGLBolAvqo/YKE7j
|
||||||
|
rcJWjRGHAgMBAAECggEABJYUCcyJcnbagytBxfnaNQUuAp8AIypFG3kipq0l5Stk
|
||||||
|
gGaTJq5F4OTGS4ofRsqeu07IgBSAfqJcJH8toPkDQqfvs6ftO1Mso2UzakMOcP51
|
||||||
|
Ywxd91Kjm+LKOyHkHGDirPGnutUg/YpLLrrMvTk/bJHDZCM4i/WP1WTREVFjUgl7
|
||||||
|
4L6Y53x2Lk5shJJhv0MzTGaoZzQcW0EbhNH1AI6MBv5/CN5m/7/+HCPlHSNKnozl
|
||||||
|
o3PXD6l0XNfOY2Hi6MgS/Vd70s3VmDT9UCJNsDjdFpKNHmI7vr9FScOLN8EwbqWe
|
||||||
|
maFa0TPknmPDmVjEGMtgGlJWL7Sm0MpNW+WsEXcDPQKBgQDv3sp0nVML9pxdzX/w
|
||||||
|
rGebFaZaMYDWmV9w0V1uXYh4ZkpFmqrWkq/QSTGpwIP/x8WH9FBDUZqspLpPBNgG
|
||||||
|
ft1XhuY34y3hoCxOyRhQcR/1dY+lgCzuN4G4MG3seq/cAXhrmkPljma/iO8KzoRK
|
||||||
|
Pa+uaKFGHy1vWY2AmOhT20zr4wKBgQDScA3478TFHg9THlSFzqpzvVn5eAvmmrCQ
|
||||||
|
RMYIZKFWPortqzeJHdA5ShVF1XBBar1yNMid7E7FXqi/P8Oh+E6Nuc7JxyVIJWlV
|
||||||
|
mcBE1ceTKdZn7A0nuQIaU6hcn7xz/UHmxGur1ZcNQm3diFJ2CPn11lzZlkSZLSCN
|
||||||
|
V86nndA9DQKBgQCWsUxXPo7xsRhDBdseg/ECyPMdLoRWTTxcT+t2bmRR31FBsQ0q
|
||||||
|
iDTTkWgV0NAcXJCH/MB/ykB1vXceNVjRm9nKJwFyktI8MLglNsiDoM4HErgPrRqM
|
||||||
|
/WoNIL+uFNVuTa4tS1jkWjXKlmg2Tc9mJKK92xWWS/frQENZSraKF/eXKQKBgGR9
|
||||||
|
ni6CUTTQZgELOtGrHzql8ZFwAj7dH/PE48yeQW0t8KoOWTbhRc4V0pLGmhSjJFSl
|
||||||
|
YCgJ8JPP4EVz7bgrG1gSou04bFVHiEWYZnh4nhVopTp7Psz5TEfGK2AP5658Ajxx
|
||||||
|
D/m+xaNPVae0sawsHTGIbE57s8ZyBll41Pa2JfsBAoGBANtS7SOehkflSdry0eAZ
|
||||||
|
50Ec3CmY+fArC044hQCmXxol5SiTUnXf/OIQH8y+RZUjF4F3PbbrFNLm/J6wuUPw
|
||||||
|
XUIb4gAL75srakL8DXqyTYfO02aNrFEHhXzMs+GoAnRkFy16IAAFwpjbYSPanfed
|
||||||
|
PfHCTWz6Y0pGdh1hUJAFV/3v
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,32 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFljCCA36gAwIBAgICEAwwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0Ux
|
||||||
|
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQL
|
||||||
|
DBBNeUludGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4X
|
||||||
|
DTIzMDMxNjIwMjAzMloXDTMzMDYyMTIwMjAzMlowdjELMAkGA1UEBhMCU0UxEjAQ
|
||||||
|
BgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQKDAlN
|
||||||
|
eU9yZ05hbWUxGTAXBgNVBAsMEE15SW50ZXJtZWRpYXRlQ0ExEDAOBgNVBAMMB0Ns
|
||||||
|
aWVudDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEOZ6fYNjZDNXX
|
||||||
|
eOyapHMOMeNeYM3b7vsWXAbiJIt4utVrTS0A+/G640t/U0g8F9jbKgbjEEPtgPJ7
|
||||||
|
GltjLWObfqDWKSO2D9/ei2+NauqgiN/HX+dQnSKHob0McXBXvLfrA4tn4braKrbg
|
||||||
|
p1fZB8bAECuT/bUhVBqWlzrUwDMpqjMJWDab48ixezb2gnc/ePE6wq/d3ecDb0/k
|
||||||
|
cYWQ0LX4JiQBgaTGhwczyoGfL1z2vx5kJqptK+r0Hc2jNCn6kFvoZUCYjCWgWNxZ
|
||||||
|
sQk7fObQQkUb/XQyqRaKJBWDyqsNcuK2gOg3LGeolAlgtMiEqGhHv77XdJnJug/w
|
||||||
|
3OiHpP/7AgMBAAGjggE3MIIBMzAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIF
|
||||||
|
oDAzBglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgQ2xpZW50IENlcnRp
|
||||||
|
ZmljYXRlMB0GA1UdDgQWBBRxZFdIkSg6zDZCakXmIest5a6dBzAfBgNVHSMEGDAW
|
||||||
|
gBRMcIY7FVKJurUPkqqusTFBE75z8zAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0lBBYw
|
||||||
|
FAYIKwYBBQUHAwIGCCsGAQUFBwMEMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9s
|
||||||
|
b2NhbGhvc3Q6OTg3OC9pbnRlcm1lZGlhdGUzLmNybC5wZW0wMQYIKwYBBQUHAQEE
|
||||||
|
JTAjMCEGCCsGAQUFBzABhhVodHRwOi8vbG9jYWxob3N0Ojk4NzcwDQYJKoZIhvcN
|
||||||
|
AQELBQADggIBAEntkhiPpQtModUF/ffnxruq+cqopPhIdMXhMD8gtU5e4e7o3EHX
|
||||||
|
lfZKIbxyw56v6dFPrl4TuHBiBudqIvBCsPtllWKixWvg6FV3CrEeTcg4shUIaJcD
|
||||||
|
pqv1qHLwS4pue6oau/lb8jv1GuzuBXoMFQwlmiOXO7xXqXjV2GdmkFJCDdB/0BW1
|
||||||
|
VHvh0DXgotaxITWKhCpSNB7F7LSvegRwZIAN6JXrLDpue7tgqLqBB1EzpmS6ALbn
|
||||||
|
uZDdikOs/tGAFB3un/3Gl7jEPL8UGOoSj/H9PUT5AFHrHJDH72+QSXu09agz8RWJ
|
||||||
|
V939njYFCAxQ8Jt2mOK8BJQDJgPtLfIIb1iYicQV13Eypt8uIUYvp0i0Wq8WxPbq
|
||||||
|
rOEvQYpcGUsreS5XqZ7y68hgq6ePiR18Fnc3GyTV5o6qT3W7IOvPArTzNV5fFCwM
|
||||||
|
lx8xSEm+ebJrJPphp6Uc/h8evohvAN8R/Z7FSo9OL6V+F3ywPqWTXaqiIiRc9PS0
|
||||||
|
0vxsYZ96EeZY5HzjN6LzHxmkv4KYM5I1qmXlviQlaU+sotp3tzegADlM4K78nUFh
|
||||||
|
HuXamecEcS73eAgjk+FGqJ9E25B0TLlQMcP6tCKdaUIGn6ZsF5wT87GzqT99wL/5
|
||||||
|
foHCYIkyG7ZmAQmoaKBd4q6xqVOUHovmsPza69FuSrsBxoRR39PtAnrY
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEOZ6fYNjZDNXX
|
||||||
|
eOyapHMOMeNeYM3b7vsWXAbiJIt4utVrTS0A+/G640t/U0g8F9jbKgbjEEPtgPJ7
|
||||||
|
GltjLWObfqDWKSO2D9/ei2+NauqgiN/HX+dQnSKHob0McXBXvLfrA4tn4braKrbg
|
||||||
|
p1fZB8bAECuT/bUhVBqWlzrUwDMpqjMJWDab48ixezb2gnc/ePE6wq/d3ecDb0/k
|
||||||
|
cYWQ0LX4JiQBgaTGhwczyoGfL1z2vx5kJqptK+r0Hc2jNCn6kFvoZUCYjCWgWNxZ
|
||||||
|
sQk7fObQQkUb/XQyqRaKJBWDyqsNcuK2gOg3LGeolAlgtMiEqGhHv77XdJnJug/w
|
||||||
|
3OiHpP/7AgMBAAECggEADSe89sig5E63SKAlFXcGw0H2XgqIzDP/TGMnqPvNoYhX
|
||||||
|
eSXUgxDhBptpB9e9a4RaKwaFxxPjlSXEdYFX9O22YSN1RMMl6Q8Zl9g3edhcDR6W
|
||||||
|
b7Qbx2x8qj6Rjibnlh8JiFPiaDjN2wUeSDBss/9D98NkKiJ9Ue2YCYmJAOA3B3w9
|
||||||
|
2t4Co5+3YrxkdzkvibTQCUSEwHFeB1Nim21126fknMPxyrf+AezRBRc8JNAHqzWb
|
||||||
|
4QEeMnmIJDOzc3Oh7+P85tNyejOeRm9T7X3EQ0jKXgLYe+HUzXclBQ66b9x9Nc9b
|
||||||
|
tNn6XkMlLlsQ3f149Th6PtHksH3hM+GF8bMuCp9yxQKBgQDGk0PYPkLqTD8jHjJW
|
||||||
|
s8wBNhozigZPGaynxdTsD7L6UtDdOl1sSW/jFOj9UIs1duBce9dP1IjFc0jY+Kin
|
||||||
|
lMLv3qCtk5ZjxRglOoLipR9hdClcM69rDoRZdoQK8KYa+QHcOTSazIp3fnw4gWSX
|
||||||
|
nscelMfd1rtVP0dOGTuqE/73/QKBgQD8+F5WAi2IOVPHnBxAAOP+6XTs9Ntn1sDi
|
||||||
|
L5wNgm+QA28aJJ4KRAwdXIc3IFPlHxZI77c2K1L9dKDu9X4UcyZIZYDvGVLuOOt5
|
||||||
|
twaRaGuJW03cjbgOWC7rGyfzfZ49YlCZi2YuxERclBkbqgWD9hfa8twUfKNguF2Y
|
||||||
|
AyiOhohtVwKBgQCJB8zUp7pzhqQ3LrpcHHzWBSi1kjTiVvxPVnSlZfwDRCz/zSv0
|
||||||
|
8wRz9tUFIZS/E0ama4tcenTblL+bgpSX+E9BSiclQOiR9su/vQ3fK0Vpccis6LnP
|
||||||
|
rdflCKT8C68Eg/slppBHloijBzTfpWLuQlJ0JwV5b5ocrKsfGMiUiHH1XQKBgQDg
|
||||||
|
RnakfEPP7TtY0g+9ssxwOJxAZImM0zmojpsk4wpzvIeovuQap9+xvFHoztFyZhBE
|
||||||
|
07oz3U8zhE4V7TI9gSVktBEOaf47U914yIqbKd+FJJywODkBBq96I1ZVKn67X0mk
|
||||||
|
B5GtTrZo+agU/bTsHKdjp0L1KtdSLcJUviAb1Cxp+wKBgDrGqS01CCgxSUwMaZe4
|
||||||
|
8HFWp/oMSyVDG9lTSC3uP/VL76zNFI55T3X06Q87hDN3gCJGUOmHzDZ/oCOgM4/S
|
||||||
|
SU55M4lXeSEdFe84tMXJKOv5JXTkulzBYzATJ5J8DeS/4YZxMKyPDLXX8wgwmU+l
|
||||||
|
i6Imd3qCPhh5eI3z9eSNDX+6
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,20 @@
|
||||||
|
-----BEGIN X509 CRL-----
|
||||||
|
MIIDPDCCASQCAQEwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0UxEjAQBgNV
|
||||||
|
BAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQLDBBNeUlu
|
||||||
|
dGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBFw0yMjA3MjAy
|
||||||
|
MDIzNTNaFw0zMjEwMjUyMDIzNTNaMBUwEwICEAIXDTIyMDYxMzEyNDIwNVqgbjBs
|
||||||
|
MB8GA1UdIwQYMBaAFCuv1TkzC1fSgTfzE1m1u5pRCJsVMDwGA1UdHAQ1MDOgLqAs
|
||||||
|
hipodHRwOi8vbG9jYWxob3N0Ojk4NzgvaW50ZXJtZWRpYXRlLmNybC5wZW2EAf8w
|
||||||
|
CwYDVR0UBAQCAhADMA0GCSqGSIb3DQEBCwUAA4ICAQBbWdqRFsIrG6coL6ln1RL+
|
||||||
|
uhgW9l3XMmjNlyiYHHNzOgnlBok9xu9UdaVCOKC6GEthWSzSlBY1AZugje57DQQd
|
||||||
|
RkIJol9am94lKMTjF/qhzFLiSjho8fwZGDGyES5YeZXkLqNMOf6m/drKaI3iofWf
|
||||||
|
l63qU9jY8dnSrVDkwgCguUL2FTx60v5H9NPxSctQ3VDxDvDj0sTAcHFknQcZbfvY
|
||||||
|
ZWpOYNS0FAJlQPVK9wUoDxI0LhrWDq5h/T1jcGO34fPT8RUA5HRtFVUevqSuOLWx
|
||||||
|
WTfTx5oDeMZPJTvHWUcg4yMElHty4tEvtkFxLSYbZqj7qTU+mi/LAN3UKBH/gBEN
|
||||||
|
y2OsJvFhVRgHf+zPYegf3WzBSoeaXNAJZ4UnRo34P9AL3Mrh+4OOUP++oYRKjWno
|
||||||
|
pYtAmTrIwEYoLyisEhhZ6aD92f/Op3dIYsxwhHt0n0lKrbTmUfiJUAe7kUZ4PMn4
|
||||||
|
Gg/OHlbEDaDxW1dCymjyRGl+3/8kjy7bkYUXCf7w6JBeL2Hw2dFp1Gh13NRjre93
|
||||||
|
PYlSOvI6QNisYGscfuYPwefXogVrNjf/ttCksMa51tUk+ylw7ZMZqQjcPPSzmwKc
|
||||||
|
5CqpnQHfolvRuN0xIVZiAn5V6/MdHm7ocrXxOkzWQyaoNODTq4js8h8eYXgAkt1w
|
||||||
|
p1PTEFBucGud7uBDE6Ub6A==
|
||||||
|
-----END X509 CRL-----
|
|
@ -0,0 +1,12 @@
|
||||||
|
crl_cache.refresh_interval = {{ refresh_interval }}
|
||||||
|
crl_cache.http_timeout = 17s
|
||||||
|
crl_cache.capacity = {{ cache_capacity }}
|
||||||
|
listeners.ssl.default {
|
||||||
|
ssl_options {
|
||||||
|
keyfile = "{{ test_data_dir }}/server.key.pem"
|
||||||
|
certfile = "{{ test_data_dir }}/server.cert.pem"
|
||||||
|
cacertfile = "{{ test_data_dir }}/ca-chain.cert.pem"
|
||||||
|
verify = verify_peer
|
||||||
|
enable_crl_check = true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
-module(emqx_crl_cache_http_server).
|
||||||
|
|
||||||
|
-behaviour(gen_server).
|
||||||
|
-compile([nowarn_export_all, export_all]).
|
||||||
|
|
||||||
|
set_crl(CRLPem) ->
|
||||||
|
ets:insert(?MODULE, {crl, CRLPem}).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% `gen_server' APIs
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
start_link(Parent, BasePort, CRLPem, Opts) ->
|
||||||
|
process_flag(trap_exit, true),
|
||||||
|
stop_http(),
|
||||||
|
timer:sleep(100),
|
||||||
|
gen_server:start_link(?MODULE, {Parent, BasePort, CRLPem, Opts}, []).
|
||||||
|
|
||||||
|
init({Parent, BasePort, CRLPem, Opts}) ->
|
||||||
|
Tab = ets:new(?MODULE, [named_table, ordered_set, public]),
|
||||||
|
ets:insert(Tab, {crl, CRLPem}),
|
||||||
|
ok = start_http(Parent, [{port, BasePort} | Opts]),
|
||||||
|
Parent ! {self(), ready},
|
||||||
|
{ok, #{parent => Parent}}.
|
||||||
|
|
||||||
|
handle_call(_Request, _From, State) ->
|
||||||
|
{reply, ignored, State}.
|
||||||
|
|
||||||
|
handle_cast(_Msg, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
terminate(_Reason, _State) ->
|
||||||
|
stop_http().
|
||||||
|
|
||||||
|
stop(Pid) ->
|
||||||
|
ok = gen_server:stop(Pid).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Callbacks
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
start_http(Parent, Opts) ->
|
||||||
|
{ok, _Pid1} = cowboy:start_clear(http, Opts, #{
|
||||||
|
env => #{dispatch => compile_router(Parent)}
|
||||||
|
}),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
stop_http() ->
|
||||||
|
cowboy:stop_listener(http),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
compile_router(Parent) ->
|
||||||
|
{ok, _} = application:ensure_all_started(cowboy),
|
||||||
|
cowboy_router:compile([
|
||||||
|
{'_', [{'_', ?MODULE, #{parent => Parent}}]}
|
||||||
|
]).
|
||||||
|
|
||||||
|
init(Req, #{parent := Parent} = State) ->
|
||||||
|
%% assert
|
||||||
|
<<"GET">> = cowboy_req:method(Req),
|
||||||
|
[{crl, CRLPem}] = ets:lookup(?MODULE, crl),
|
||||||
|
Parent ! {http_get, iolist_to_binary(cowboy_req:uri(Req))},
|
||||||
|
Reply = reply(Req, CRLPem),
|
||||||
|
{ok, Reply, State}.
|
||||||
|
|
||||||
|
reply(Req, CRLPem) ->
|
||||||
|
cowboy_req:reply(200, #{<<"content-type">> => <<"text/plain">>}, CRLPem, Req).
|
|
@ -0,0 +1,12 @@
|
||||||
|
node.name = test@127.0.0.1
|
||||||
|
node.cookie = emqxsecretcookie
|
||||||
|
node.data_dir = "{{ test_priv_dir }}"
|
||||||
|
listeners.ssl.default {
|
||||||
|
ssl_options {
|
||||||
|
keyfile = "{{ test_data_dir }}/server.key.pem"
|
||||||
|
certfile = "{{ test_data_dir }}/server.cert.pem"
|
||||||
|
cacertfile = "{{ test_data_dir }}/ca-chain.cert.pem"
|
||||||
|
verify = verify_peer
|
||||||
|
enable_crl_check = false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
-----BEGIN X509 CRL-----
|
||||||
|
MIIDJTCCAQ0CAQEwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0UxEjAQBgNV
|
||||||
|
BAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQLDBBNeUlu
|
||||||
|
dGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBFw0yMzAxMTIx
|
||||||
|
MzA4MTZaFw0zMzAxMDkxMzA4MTZaoG4wbDAfBgNVHSMEGDAWgBRMcIY7FVKJurUP
|
||||||
|
kqqusTFBE75z8zA8BgNVHRwENTAzoC6gLIYqaHR0cDovL2xvY2FsaG9zdDo5ODc4
|
||||||
|
L2ludGVybWVkaWF0ZS5jcmwucGVthAH/MAsGA1UdFAQEAgIQADANBgkqhkiG9w0B
|
||||||
|
AQsFAAOCAgEAJGOZuqZL4m7zUaRyBrxeT6Tqo+XKz7HeD5zvO4BTNX+0E0CRyki4
|
||||||
|
HhIGbxjv2NKWoaUv0HYbGAiZdO4TaPu3w3tm4+pGEDBclBj2KTdbB+4Hlzv956gD
|
||||||
|
KXZ//ziNwx1SCoxxkxB+TALxReN0shE7Mof9GlB5HPskhLorZgg/pmgJtIykEpsq
|
||||||
|
QAjJo4aq+f2/L+9dzRM205fVFegtsHvgEVNKz6iK6skt+kDhj/ks9BKsnfCDIGr+
|
||||||
|
XnPYwS9yDnnhFdoJ40AQQDtomxggAjfgcSnqtHCxZwKJohuztbSWUgD/4yxzlrwP
|
||||||
|
Dk1cT/Ajjjqb2dXVOfTLK1VB2168uuouArxZ7KYbXwBjHduYWGGkA6FfkNJO/jpF
|
||||||
|
SL9qhX3oxcRF3hDhWigN1ZRD7NpDKwVal3Y9tmvO5bWhb5VF+3qv0HGeSGp6V0dp
|
||||||
|
sjwhIj+78bkUrcXxrivACLAXgSTGonx1uXD+T4P4NCt148dgRAbgd8sUXK5FcgU2
|
||||||
|
cdBl8Kv2ZUjEaod5gUzDtf22VGSoO9lHvfHdpG9o2H3wC7s4tyLTidNrduIguJff
|
||||||
|
IIgc44Y252iV0sOmZ5S0jjTRiF1YUUPy9qA/6bOnr2LohbwbNZv9tDlNj8cdhxUz
|
||||||
|
cKiS+c7Qsz+YCcrp19QRiJoQae/gUqz7kmUZQgyPmDd+ArE0V+kDZEE=
|
||||||
|
-----END X509 CRL-----
|
|
@ -0,0 +1,19 @@
|
||||||
|
-----BEGIN X509 CRL-----
|
||||||
|
MIIC/TCB5gIBATANBgkqhkiG9w0BAQsFADBrMQswCQYDVQQGEwJTRTESMBAGA1UE
|
||||||
|
CAwJU3RvY2tob2xtMRIwEAYDVQQKDAlNeU9yZ05hbWUxGTAXBgNVBAsMEE15SW50
|
||||||
|
ZXJtZWRpYXRlQ0ExGTAXBgNVBAMMEE15SW50ZXJtZWRpYXRlQ0EXDTIzMDExODEz
|
||||||
|
Mjc1M1oXDTMzMDExNTEzMjc1M1owFTATAgIQAhcNMjMwMTEyMTMwODE2WqAwMC4w
|
||||||
|
HwYDVR0jBBgwFoAUTHCGOxVSibq1D5KqrrExQRO+c/MwCwYDVR0UBAQCAhACMA0G
|
||||||
|
CSqGSIb3DQEBCwUAA4ICAQCxoRYDc5MaBpDI+HQUX60+obFeZJdBkPO2wMb6HBQq
|
||||||
|
e0lZM2ukS+4n5oGhRelsvmEz0qKvnYS6ISpuFzv4Qy6Vaun/KwIYAdXsEQVwDHsu
|
||||||
|
Br4m1V01igjFnujowwR/7F9oPnZOmBaBdiyYbjgGV0YMF7sOfl4UO2MqI2GSGqVk
|
||||||
|
63wELT1AXjx31JVoyATQOQkq1A5HKFYLEbFmdF/8lNfbxSCBY2tuJ+uWVQtzjM0y
|
||||||
|
i+/owz5ez1BZ/Swx8akYhuvs8DVVTbjXydidVSrxt/QEf3+oJCzTA9qFqt4MH7gL
|
||||||
|
6BAglCGtRiYTHqeMHrwddaHF2hzR61lHJlkMCL61yhVuL8WsEJ/AxVX0W3MfQ4Cw
|
||||||
|
x/A6xIkgqtu+HtQnPyDcJxyaFHtFC+U67nSbEQySFvHfMw42DGdIGojKQCeUer9W
|
||||||
|
ECFC8OATQwN2h//f8QkY7D0H3k/brrNYDfdFIcCti9iZiFrrPFxO7NbOTfkeKCt3
|
||||||
|
7IwYduRc8DWKmS8c7j2re1KkdYnfE1sfwbn3trImkcET5tvDlVCZ1glnBQzk82PS
|
||||||
|
HvKmSjD2pZI7upfLkoMgMhYyYJhYk7Mw2o4JXuddYGKmmw3bJyHkG/Ot5NAKjb7g
|
||||||
|
k1QCeWzxO1xXm8PNDDFWMn351twUGDQ/cwrUw0ODeUZpfL0BtTn4YnfCLLTvZDxo
|
||||||
|
Vg==
|
||||||
|
-----END X509 CRL-----
|
|
@ -0,0 +1,20 @@
|
||||||
|
-----BEGIN X509 CRL-----
|
||||||
|
MIIDPDCCASQCAQEwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0UxEjAQBgNV
|
||||||
|
BAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQLDBBNeUlu
|
||||||
|
dGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBFw0yMzAxMTIx
|
||||||
|
MzA4MTZaFw0zMzAxMDkxMzA4MTZaMBUwEwICEAIXDTIzMDExMjEzMDgxNlqgbjBs
|
||||||
|
MB8GA1UdIwQYMBaAFExwhjsVUom6tQ+Sqq6xMUETvnPzMDwGA1UdHAQ1MDOgLqAs
|
||||||
|
hipodHRwOi8vbG9jYWxob3N0Ojk4NzgvaW50ZXJtZWRpYXRlLmNybC5wZW2EAf8w
|
||||||
|
CwYDVR0UBAQCAhABMA0GCSqGSIb3DQEBCwUAA4ICAQCPadbaehEqLv4pwqF8em8T
|
||||||
|
CW8TOQ4Vjz02uiVk9Bo0za1dQqQmwCBA6UE1BcOh+aWzQxBRz56NeUcfhgDxTntG
|
||||||
|
xLs896N9MHIG6UxpqJH8cH+DXKHsQjvvCjXtiObmBQR1RiG5C1vEMkfzTt/WSrq5
|
||||||
|
7blowLDs4NP6YbtqXEyyUkF7DQSUEUuIDWPQdx1f++nSpVaHWW4xpoO4umesaJco
|
||||||
|
FuxaXQnZpTHHQfqUJVIL2Mmzvez9thgfKTV3vgkYrGiSLW2m2+Tfga30pUc0qaVI
|
||||||
|
RrBVORVbcu9m1sV0aJyk96b2T/+i2FRR/np4TOcLgckBpHKeK2FH69lHFr0W/71w
|
||||||
|
CErNTxahoh82Yi8POenu+S1m2sDnrF1FMf+ZG/i2wr0nW6/+zVGQsEOw77Spbmei
|
||||||
|
dbEchu3iWF1XEO/n4zVBzl6a1o2RyVg+1pItYd5C5bPwcrfZnBrm4WECPxO+6rbW
|
||||||
|
2/wz9Iku4XznTLqLEpXLAtenAdo73mLGC7riviX7mhcxfN2UjNfLuVGHmG8XwIsM
|
||||||
|
Lgpr6DKaxHwpHgW3wA3SGJrY5dj0TvGWaoInrNt1cOMnIpoxRNy5+ko71Ubx3yrV
|
||||||
|
RhbUMggd1GG1ct9uZn82v74RYF6J8Xcxn9vDFJu5LLT5kvfy414kdJeTXKqfKXA/
|
||||||
|
atdUgFa0otoccn5FzyUuzg==
|
||||||
|
-----END X509 CRL-----
|
|
@ -0,0 +1,20 @@
|
||||||
|
-----BEGIN X509 CRL-----
|
||||||
|
MIIDPDCCASQCAQEwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0UxEjAQBgNV
|
||||||
|
BAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQLDBBNeUlu
|
||||||
|
dGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBFw0yMjA3MjAy
|
||||||
|
MDIzNTNaFw0zMjEwMjUyMDIzNTNaMBUwEwICEAIXDTIyMDYxMzEyNDIwNVqgbjBs
|
||||||
|
MB8GA1UdIwQYMBaAFCuv1TkzC1fSgTfzE1m1u5pRCJsVMDwGA1UdHAQ1MDOgLqAs
|
||||||
|
hipodHRwOi8vbG9jYWxob3N0Ojk4NzgvaW50ZXJtZWRpYXRlLmNybC5wZW2EAf8w
|
||||||
|
CwYDVR0UBAQCAhADMA0GCSqGSIb3DQEBCwUAA4ICAQBbWdqRFsIrG6coL6ln1RL+
|
||||||
|
uhgW9l3XMmjNlyiYHHNzOgnlBok9xu9UdaVCOKC6GEthWSzSlBY1AZugje57DQQd
|
||||||
|
RkIJol9am94lKMTjF/qhzFLiSjho8fwZGDGyES5YeZXkLqNMOf6m/drKaI3iofWf
|
||||||
|
l63qU9jY8dnSrVDkwgCguUL2FTx60v5H9NPxSctQ3VDxDvDj0sTAcHFknQcZbfvY
|
||||||
|
ZWpOYNS0FAJlQPVK9wUoDxI0LhrWDq5h/T1jcGO34fPT8RUA5HRtFVUevqSuOLWx
|
||||||
|
WTfTx5oDeMZPJTvHWUcg4yMElHty4tEvtkFxLSYbZqj7qTU+mi/LAN3UKBH/gBEN
|
||||||
|
y2OsJvFhVRgHf+zPYegf3WzBSoeaXNAJZ4UnRo34P9AL3Mrh+4OOUP++oYRKjWno
|
||||||
|
pYtAmTrIwEYoLyisEhhZ6aD92f/Op3dIYsxwhHt0n0lKrbTmUfiJUAe7kUZ4PMn4
|
||||||
|
Gg/OHlbEDaDxW1dCymjyRGl+3/8kjy7bkYUXCf7w6JBeL2Hw2dFp1Gh13NRjre93
|
||||||
|
PYlSOvI6QNisYGscfuYPwefXogVrNjf/ttCksMa51tUk+ylw7ZMZqQjcPPSzmwKc
|
||||||
|
5CqpnQHfolvRuN0xIVZiAn5V6/MdHm7ocrXxOkzWQyaoNODTq4js8h8eYXgAkt1w
|
||||||
|
p1PTEFBucGud7uBDE6Ub6A==
|
||||||
|
-----END X509 CRL-----
|
|
@ -0,0 +1,35 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIGCTCCA/GgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0Ux
|
||||||
|
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQL
|
||||||
|
DBBNeUludGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4X
|
||||||
|
DTIzMDExMjEzMDgxNloXDTMzMDQxOTEzMDgxNloweDELMAkGA1UEBhMCU0UxEjAQ
|
||||||
|
BgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQKDAlN
|
||||||
|
eU9yZ05hbWUxGTAXBgNVBAsMEE15SW50ZXJtZWRpYXRlQ0ExEjAQBgNVBAMMCWxv
|
||||||
|
Y2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKdU9FaA/n0Z
|
||||||
|
TXkd10XA9l+UV9xKR65ZTy2ApCFlw2gGWLiUh96a6hX+GQZFUV7ECIDDf+7nC85o
|
||||||
|
xo1Xyf0rHGABQ0uHlhqSemc12F9APIzRLlQkhtV4vMBBbGQFekje4F9bhY9JQtGd
|
||||||
|
XJGmwsR+XWo6SUY7K5l9FuSSSRXC0kSYYQfSTPR/LrF6efdHf+ZN4huP7lM2qIFd
|
||||||
|
afX+qBOI1/Y2LtITo2TaU/hXyKh9wEiuynoq0RZ2KkYQll5cKD9fSD+pW3Xm0XWX
|
||||||
|
TQy4RZEe3WoYEQsklNw3NC92ocA/PQB9BGNO1fKhzDn6kW2HxDxruDKOuO/meGek
|
||||||
|
ApCayu3e/I0CAwEAAaOCAagwggGkMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQD
|
||||||
|
AgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2Vy
|
||||||
|
dGlmaWNhdGUwHQYDVR0OBBYEFGy5LQPzIelruJl7mL0mtUXM57XhMIGaBgNVHSME
|
||||||
|
gZIwgY+AFExwhjsVUom6tQ+Sqq6xMUETvnPzoXOkcTBvMQswCQYDVQQGEwJTRTES
|
||||||
|
MBAGA1UECAwJU3RvY2tob2xtMRIwEAYDVQQHDAlTdG9ja2hvbG0xEjAQBgNVBAoM
|
||||||
|
CU15T3JnTmFtZTERMA8GA1UECwwITXlSb290Q0ExETAPBgNVBAMMCE15Um9vdENB
|
||||||
|
ggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwOwYDVR0f
|
||||||
|
BDQwMjAwoC6gLIYqaHR0cDovL2xvY2FsaG9zdDo5ODc4L2ludGVybWVkaWF0ZS5j
|
||||||
|
cmwucGVtMDEGCCsGAQUFBwEBBCUwIzAhBggrBgEFBQcwAYYVaHR0cDovL2xvY2Fs
|
||||||
|
aG9zdDo5ODc3MA0GCSqGSIb3DQEBCwUAA4ICAQCX3EQgiCVqLhnCNd0pmptxXPxo
|
||||||
|
l1KyZkpdrFa/NgSqRhkuZSAkszwBDDS/gzkHFKEUhmqs6/UZwN4+Rr3LzrHonBiN
|
||||||
|
aQ6GeNNXZ/3xAQfUCwjjGmz9Sgw6kaX19Gnk2CjI6xP7T+O5UmsMI9hHUepC9nWa
|
||||||
|
XX2a0hsO/KOVu5ZZckI16Ek/jxs2/HEN0epYdvjKFAaVmzZZ5PATNjrPQXvPmq2r
|
||||||
|
x++La+3bXZsrH8P2FhPpM5t/IxKKW/Tlpgz92c2jVSIHF5khSA/MFDC+dk80OFmm
|
||||||
|
v4ZTPIMuZ//Q+wo0f9P48rsL9D27qS7CA+8pn9wu+cfnBDSt7JD5Yipa1gHz71fy
|
||||||
|
YTa9qRxIAPpzW2v7TFZE8eSKFUY9ipCeM2BbdmCQGmq4+v36b5TZoyjH4k0UVWGo
|
||||||
|
Gclos2cic5Vxi8E6hb7b7yZpjEfn/5lbCiGMfAnI6aoOyrWg6keaRA33kaLUEZiK
|
||||||
|
OgFNbPkjiTV0ZQyLXf7uK9YFhpVzJ0dv0CFNse8rZb7A7PLn8VrV/ZFnJ9rPoawn
|
||||||
|
t7ZGxC0d5BRSEyEeEgsQdxuY4m8OkE18zwhCkt2Qs3uosOWlIrYmqSEa0i/sPSQP
|
||||||
|
jiwB4nEdBrf8ZygzuYjT5T9YRSwhVox4spS/Av8Ells5JnkuKAhCVv9gHxYwbj0c
|
||||||
|
CzyLJgE1z9Tq63m+gQ==
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCnVPRWgP59GU15
|
||||||
|
HddFwPZflFfcSkeuWU8tgKQhZcNoBli4lIfemuoV/hkGRVFexAiAw3/u5wvOaMaN
|
||||||
|
V8n9KxxgAUNLh5YaknpnNdhfQDyM0S5UJIbVeLzAQWxkBXpI3uBfW4WPSULRnVyR
|
||||||
|
psLEfl1qOklGOyuZfRbkkkkVwtJEmGEH0kz0fy6xenn3R3/mTeIbj+5TNqiBXWn1
|
||||||
|
/qgTiNf2Ni7SE6Nk2lP4V8iofcBIrsp6KtEWdipGEJZeXCg/X0g/qVt15tF1l00M
|
||||||
|
uEWRHt1qGBELJJTcNzQvdqHAPz0AfQRjTtXyocw5+pFth8Q8a7gyjrjv5nhnpAKQ
|
||||||
|
msrt3vyNAgMBAAECggEABnWvIQ/Fw0qQxRYz00uJt1LguW5cqgxklBsdOvTUwFVO
|
||||||
|
Y4HIZP2R/9tZV/ahF4l10pK5g52DxSoiUB6Ne6qIY+RolqfbUZdKBmX7vmGadM02
|
||||||
|
fqUSV3dbwghEiO/1Mo74FnZQB6IKZFEw26aWakN+k7VAUufB3SEJGzXSgHaO63ru
|
||||||
|
dFGSiYI8U+q+YnhUJjCnmI12fycNfy451TdUQtGZb6pNmm5HRUF6hpAV8Le9LojP
|
||||||
|
Ql9eacPpsrzU15X5ElCQZ/f9iNh1bplcISuhrULgKUKOvAVrBlEK67uRVy6g98xA
|
||||||
|
c/rgNLkbL/jZEsAc3/vHAyFgd3lABfwpBGLHej3QgQKBgQDFNYmfBNQr89HC5Zc+
|
||||||
|
M6jXcAT/R+0GNczBTfC4iyNemwqsumSSRelNZ748UefKuS3F6Mvb2CBqE2LbB61G
|
||||||
|
hrnCffG2pARjZ491SefRwghhWWVGLP1p8KliLgOGBehA1REgJb+XULncjuHZuh4O
|
||||||
|
LVn3HVnWGxeBGg+yKa6Z4YQi3QKBgQDZN0O8ZcZY74lRJ0UjscD9mJ1yHlsssZag
|
||||||
|
njkX/f0GR/iVpfaIxQNC3gvWUy2LsU0He9sidcB0cfej0j/qZObQyFsCB0+utOgy
|
||||||
|
+hX7gokV2pes27WICbNWE2lJL4QZRJgvf82OaEy57kfDrm+eK1XaSZTZ10P82C9u
|
||||||
|
gAmMnontcQKBgGu29lhY9tqa7jOZ26Yp6Uri8JfO3XPK5u+edqEVvlfqL0Zw+IW8
|
||||||
|
kdWpmIqx4f0kcA/tO4v03J+TvycLZmVjKQtGZ0PvCkaRRhY2K9yyMomZnmtaH4BB
|
||||||
|
5wKtR1do2pauyg/ZDnDDswD5OfsGYWw08TK8YVlEqu3lIjWZ9rguKVIxAoGAZYUk
|
||||||
|
zVqr10ks3pcCA2rCjkPT4lA5wKvHgI4ylPoKVfMxRY/pp4acvZXV5ne9o7pcDBFh
|
||||||
|
G7v5FPNnEFPlt4EtN4tMragJH9hBZgHoYEJkG6islweg0lHmVWaBIMlqbfzXO+v5
|
||||||
|
gINSyNuLAvP2CvCqEXmubhnkFrpbgMOqsuQuBqECgYB3ss2PDhBF+5qoWgqymFof
|
||||||
|
1ovRPuQ9sPjWBn5IrCdoYITDnbBzBZERx7GLs6A/PUlWgST7jkb1PY/TxYSUfXzJ
|
||||||
|
SNd47q0mCQ+IUdqUbHgpK9b1ncwLMsnexpYZdHJWRLgnUhOx7OMjJc/4iLCAFCoN
|
||||||
|
3KJ7/V1keo7GBHOwnsFcCA==
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -76,7 +76,7 @@ init_per_testcase(t_openssl_client, Config) ->
|
||||||
[],
|
[],
|
||||||
Handler,
|
Handler,
|
||||||
#{
|
#{
|
||||||
extra_mustache_vars => [{test_data_dir, DataDir}],
|
extra_mustache_vars => #{test_data_dir => DataDir},
|
||||||
conf_file_path => ConfFilePath
|
conf_file_path => ConfFilePath
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
-include_lib("emqx/include/emqx_placeholder.hrl").
|
-include_lib("emqx/include/emqx_placeholder.hrl").
|
||||||
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
||||||
|
|
||||||
|
-import(emqx_common_test_helpers, [on_exit/1]).
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
|
@ -65,6 +67,7 @@ end_per_suite(_Config) ->
|
||||||
|
|
||||||
init_per_testcase(TestCase, Config) when
|
init_per_testcase(TestCase, Config) when
|
||||||
TestCase =:= t_subscribe_deny_disconnect_publishes_last_will_testament;
|
TestCase =:= t_subscribe_deny_disconnect_publishes_last_will_testament;
|
||||||
|
TestCase =:= t_publish_last_will_testament_banned_client_connecting;
|
||||||
TestCase =:= t_publish_deny_disconnect_publishes_last_will_testament
|
TestCase =:= t_publish_deny_disconnect_publishes_last_will_testament
|
||||||
->
|
->
|
||||||
{ok, _} = emqx_authz:update(?CMD_REPLACE, []),
|
{ok, _} = emqx_authz:update(?CMD_REPLACE, []),
|
||||||
|
@ -76,11 +79,15 @@ init_per_testcase(_, Config) ->
|
||||||
|
|
||||||
end_per_testcase(TestCase, _Config) when
|
end_per_testcase(TestCase, _Config) when
|
||||||
TestCase =:= t_subscribe_deny_disconnect_publishes_last_will_testament;
|
TestCase =:= t_subscribe_deny_disconnect_publishes_last_will_testament;
|
||||||
|
TestCase =:= t_publish_last_will_testament_banned_client_connecting;
|
||||||
TestCase =:= t_publish_deny_disconnect_publishes_last_will_testament
|
TestCase =:= t_publish_deny_disconnect_publishes_last_will_testament
|
||||||
->
|
->
|
||||||
{ok, _} = emqx:update_config([authorization, deny_action], ignore),
|
{ok, _} = emqx:update_config([authorization, deny_action], ignore),
|
||||||
|
{ok, _} = emqx_authz:update(?CMD_REPLACE, []),
|
||||||
|
emqx_common_test_helpers:call_janitor(),
|
||||||
ok;
|
ok;
|
||||||
end_per_testcase(_TestCase, _Config) ->
|
end_per_testcase(_TestCase, _Config) ->
|
||||||
|
emqx_common_test_helpers:call_janitor(),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
set_special_configs(emqx_authz) ->
|
set_special_configs(emqx_authz) ->
|
||||||
|
@ -396,5 +403,63 @@ t_publish_last_will_testament_denied_topic(_Config) ->
|
||||||
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
%% client is allowed by ACL to publish to its LWT topic, is connected,
|
||||||
|
%% and then gets banned and kicked out while connected. Should not
|
||||||
|
%% publish LWT.
|
||||||
|
t_publish_last_will_testament_banned_client_connecting(_Config) ->
|
||||||
|
{ok, _} = emqx_authz:update(?CMD_REPLACE, [?SOURCE7]),
|
||||||
|
Username = <<"some_client">>,
|
||||||
|
ClientId = <<"some_clientid">>,
|
||||||
|
LWTPayload = <<"should not be published">>,
|
||||||
|
LWTTopic = <<"some_client/lwt">>,
|
||||||
|
ok = emqx:subscribe(<<"some_client/lwt">>),
|
||||||
|
{ok, C} = emqtt:start_link([
|
||||||
|
{clientid, ClientId},
|
||||||
|
{username, Username},
|
||||||
|
{will_topic, LWTTopic},
|
||||||
|
{will_payload, LWTPayload}
|
||||||
|
]),
|
||||||
|
?assertMatch({ok, _}, emqtt:connect(C)),
|
||||||
|
|
||||||
|
%% Now we ban the client while it is connected.
|
||||||
|
Now = erlang:system_time(second),
|
||||||
|
Who = {username, Username},
|
||||||
|
emqx_banned:create(#{
|
||||||
|
who => Who,
|
||||||
|
by => <<"test">>,
|
||||||
|
reason => <<"test">>,
|
||||||
|
at => Now,
|
||||||
|
until => Now + 120
|
||||||
|
}),
|
||||||
|
on_exit(fun() -> emqx_banned:delete(Who) end),
|
||||||
|
%% Now kick it as we do in the ban API.
|
||||||
|
process_flag(trap_exit, true),
|
||||||
|
?check_trace(
|
||||||
|
begin
|
||||||
|
ok = emqx_cm:kick_session(ClientId),
|
||||||
|
receive
|
||||||
|
{deliver, LWTTopic, #message{payload = LWTPayload}} ->
|
||||||
|
error(lwt_should_not_be_published_to_forbidden_topic)
|
||||||
|
after 2_000 -> ok
|
||||||
|
end,
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
fun(Trace) ->
|
||||||
|
?assertMatch(
|
||||||
|
[
|
||||||
|
#{
|
||||||
|
client_banned := true,
|
||||||
|
publishing_disallowed := false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
?of_kind(last_will_testament_publish_denied, Trace)
|
||||||
|
),
|
||||||
|
ok
|
||||||
|
end
|
||||||
|
),
|
||||||
|
ok = snabbkaffe:stop(),
|
||||||
|
|
||||||
|
ok.
|
||||||
|
|
||||||
stop_apps(Apps) ->
|
stop_apps(Apps) ->
|
||||||
lists:foreach(fun application:stop/1, Apps).
|
lists:foreach(fun application:stop/1, Apps).
|
||||||
|
|
|
@ -54,6 +54,17 @@ emqx_bridge_schema {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
desc_status_reason {
|
||||||
|
desc {
|
||||||
|
en: "This is the reason given in case a bridge is failing to connect."
|
||||||
|
zh: "桥接连接失败的原因。"
|
||||||
|
}
|
||||||
|
label: {
|
||||||
|
en: "Failure reason"
|
||||||
|
zh: "失败原因"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
desc_node_status {
|
desc_node_status {
|
||||||
desc {
|
desc {
|
||||||
en: """The status of the bridge for each node.
|
en: """The status of the bridge for each node.
|
||||||
|
|
|
@ -67,7 +67,8 @@
|
||||||
T == timescale;
|
T == timescale;
|
||||||
T == matrix;
|
T == matrix;
|
||||||
T == tdengine;
|
T == tdengine;
|
||||||
T == dynamo
|
T == dynamo;
|
||||||
|
T == rocketmq
|
||||||
).
|
).
|
||||||
|
|
||||||
load() ->
|
load() ->
|
||||||
|
|
|
@ -46,18 +46,33 @@
|
||||||
|
|
||||||
-export([lookup_from_local_node/2]).
|
-export([lookup_from_local_node/2]).
|
||||||
|
|
||||||
-define(BAD_REQUEST(Reason), {400, error_msg('BAD_REQUEST', Reason)}).
|
%% [TODO] Move those to a commonly shared header file
|
||||||
|
-define(ERROR_MSG(CODE, REASON), #{code => CODE, message => emqx_misc:readable_error_msg(REASON)}).
|
||||||
|
|
||||||
|
-define(OK(CONTENT), {200, CONTENT}).
|
||||||
|
|
||||||
|
-define(NO_CONTENT, 204).
|
||||||
|
|
||||||
|
-define(BAD_REQUEST(CODE, REASON), {400, ?ERROR_MSG(CODE, REASON)}).
|
||||||
|
-define(BAD_REQUEST(REASON), ?BAD_REQUEST('BAD_REQUEST', REASON)).
|
||||||
|
|
||||||
|
-define(NOT_FOUND(REASON), {404, ?ERROR_MSG('NOT_FOUND', REASON)}).
|
||||||
|
|
||||||
|
-define(INTERNAL_ERROR(REASON), {500, ?ERROR_MSG('INTERNAL_ERROR', REASON)}).
|
||||||
|
|
||||||
|
-define(NOT_IMPLEMENTED, 501).
|
||||||
|
|
||||||
|
-define(SERVICE_UNAVAILABLE(REASON), {503, ?ERROR_MSG('SERVICE_UNAVAILABLE', REASON)}).
|
||||||
|
%% End TODO
|
||||||
|
|
||||||
-define(BRIDGE_NOT_ENABLED,
|
-define(BRIDGE_NOT_ENABLED,
|
||||||
?BAD_REQUEST(<<"Forbidden operation, bridge not enabled">>)
|
?BAD_REQUEST(<<"Forbidden operation, bridge not enabled">>)
|
||||||
).
|
).
|
||||||
|
|
||||||
-define(NOT_FOUND(Reason), {404, error_msg('NOT_FOUND', Reason)}).
|
-define(BRIDGE_NOT_FOUND(BRIDGE_TYPE, BRIDGE_NAME),
|
||||||
|
|
||||||
-define(BRIDGE_NOT_FOUND(BridgeType, BridgeName),
|
|
||||||
?NOT_FOUND(
|
?NOT_FOUND(
|
||||||
<<"Bridge lookup failed: bridge named '", (BridgeName)/binary, "' of type ",
|
<<"Bridge lookup failed: bridge named '", (BRIDGE_NAME)/binary, "' of type ",
|
||||||
(bin(BridgeType))/binary, " does not exist.">>
|
(bin(BRIDGE_TYPE))/binary, " does not exist.">>
|
||||||
)
|
)
|
||||||
).
|
).
|
||||||
|
|
||||||
|
@ -284,7 +299,7 @@ schema("/bridges") ->
|
||||||
'operationId' => '/bridges',
|
'operationId' => '/bridges',
|
||||||
get => #{
|
get => #{
|
||||||
tags => [<<"bridges">>],
|
tags => [<<"bridges">>],
|
||||||
summary => <<"List Bridges">>,
|
summary => <<"List bridges">>,
|
||||||
description => ?DESC("desc_api1"),
|
description => ?DESC("desc_api1"),
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => emqx_dashboard_swagger:schema_with_example(
|
200 => emqx_dashboard_swagger:schema_with_example(
|
||||||
|
@ -295,7 +310,7 @@ schema("/bridges") ->
|
||||||
},
|
},
|
||||||
post => #{
|
post => #{
|
||||||
tags => [<<"bridges">>],
|
tags => [<<"bridges">>],
|
||||||
summary => <<"Create Bridge">>,
|
summary => <<"Create bridge">>,
|
||||||
description => ?DESC("desc_api2"),
|
description => ?DESC("desc_api2"),
|
||||||
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
emqx_bridge_schema:post_request(),
|
emqx_bridge_schema:post_request(),
|
||||||
|
@ -312,7 +327,7 @@ schema("/bridges/:id") ->
|
||||||
'operationId' => '/bridges/:id',
|
'operationId' => '/bridges/:id',
|
||||||
get => #{
|
get => #{
|
||||||
tags => [<<"bridges">>],
|
tags => [<<"bridges">>],
|
||||||
summary => <<"Get Bridge">>,
|
summary => <<"Get bridge">>,
|
||||||
description => ?DESC("desc_api3"),
|
description => ?DESC("desc_api3"),
|
||||||
parameters => [param_path_id()],
|
parameters => [param_path_id()],
|
||||||
responses => #{
|
responses => #{
|
||||||
|
@ -322,7 +337,7 @@ schema("/bridges/:id") ->
|
||||||
},
|
},
|
||||||
put => #{
|
put => #{
|
||||||
tags => [<<"bridges">>],
|
tags => [<<"bridges">>],
|
||||||
summary => <<"Update Bridge">>,
|
summary => <<"Update bridge">>,
|
||||||
description => ?DESC("desc_api4"),
|
description => ?DESC("desc_api4"),
|
||||||
parameters => [param_path_id()],
|
parameters => [param_path_id()],
|
||||||
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
@ -337,7 +352,7 @@ schema("/bridges/:id") ->
|
||||||
},
|
},
|
||||||
delete => #{
|
delete => #{
|
||||||
tags => [<<"bridges">>],
|
tags => [<<"bridges">>],
|
||||||
summary => <<"Delete Bridge">>,
|
summary => <<"Delete bridge">>,
|
||||||
description => ?DESC("desc_api5"),
|
description => ?DESC("desc_api5"),
|
||||||
parameters => [param_path_id()],
|
parameters => [param_path_id()],
|
||||||
responses => #{
|
responses => #{
|
||||||
|
@ -356,7 +371,7 @@ schema("/bridges/:id/metrics") ->
|
||||||
'operationId' => '/bridges/:id/metrics',
|
'operationId' => '/bridges/:id/metrics',
|
||||||
get => #{
|
get => #{
|
||||||
tags => [<<"bridges">>],
|
tags => [<<"bridges">>],
|
||||||
summary => <<"Get Bridge Metrics">>,
|
summary => <<"Get bridge metrics">>,
|
||||||
description => ?DESC("desc_bridge_metrics"),
|
description => ?DESC("desc_bridge_metrics"),
|
||||||
parameters => [param_path_id()],
|
parameters => [param_path_id()],
|
||||||
responses => #{
|
responses => #{
|
||||||
|
@ -370,7 +385,7 @@ schema("/bridges/:id/metrics/reset") ->
|
||||||
'operationId' => '/bridges/:id/metrics/reset',
|
'operationId' => '/bridges/:id/metrics/reset',
|
||||||
put => #{
|
put => #{
|
||||||
tags => [<<"bridges">>],
|
tags => [<<"bridges">>],
|
||||||
summary => <<"Reset Bridge Metrics">>,
|
summary => <<"Reset bridge metrics">>,
|
||||||
description => ?DESC("desc_api6"),
|
description => ?DESC("desc_api6"),
|
||||||
parameters => [param_path_id()],
|
parameters => [param_path_id()],
|
||||||
responses => #{
|
responses => #{
|
||||||
|
@ -385,7 +400,7 @@ schema("/bridges/:id/enable/:enable") ->
|
||||||
put =>
|
put =>
|
||||||
#{
|
#{
|
||||||
tags => [<<"bridges">>],
|
tags => [<<"bridges">>],
|
||||||
summary => <<"Enable or Disable Bridge">>,
|
summary => <<"Enable or disable bridge">>,
|
||||||
desc => ?DESC("desc_enable_bridge"),
|
desc => ?DESC("desc_enable_bridge"),
|
||||||
parameters => [param_path_id(), param_path_enable()],
|
parameters => [param_path_id(), param_path_enable()],
|
||||||
responses =>
|
responses =>
|
||||||
|
@ -401,7 +416,7 @@ schema("/bridges/:id/:operation") ->
|
||||||
'operationId' => '/bridges/:id/:operation',
|
'operationId' => '/bridges/:id/:operation',
|
||||||
post => #{
|
post => #{
|
||||||
tags => [<<"bridges">>],
|
tags => [<<"bridges">>],
|
||||||
summary => <<"Stop or Restart Bridge">>,
|
summary => <<"Stop or restart bridge">>,
|
||||||
description => ?DESC("desc_api7"),
|
description => ?DESC("desc_api7"),
|
||||||
parameters => [
|
parameters => [
|
||||||
param_path_id(),
|
param_path_id(),
|
||||||
|
@ -423,7 +438,7 @@ schema("/nodes/:node/bridges/:id/:operation") ->
|
||||||
'operationId' => '/nodes/:node/bridges/:id/:operation',
|
'operationId' => '/nodes/:node/bridges/:id/:operation',
|
||||||
post => #{
|
post => #{
|
||||||
tags => [<<"bridges">>],
|
tags => [<<"bridges">>],
|
||||||
summary => <<"Stop/Restart Bridge">>,
|
summary => <<"Stop/restart bridge">>,
|
||||||
description => ?DESC("desc_api8"),
|
description => ?DESC("desc_api8"),
|
||||||
parameters => [
|
parameters => [
|
||||||
param_path_node(),
|
param_path_node(),
|
||||||
|
@ -463,11 +478,10 @@ schema("/bridges_probe") ->
|
||||||
'/bridges'(post, #{body := #{<<"type">> := BridgeType, <<"name">> := BridgeName} = Conf0}) ->
|
'/bridges'(post, #{body := #{<<"type">> := BridgeType, <<"name">> := BridgeName} = Conf0}) ->
|
||||||
case emqx_bridge:lookup(BridgeType, BridgeName) of
|
case emqx_bridge:lookup(BridgeType, BridgeName) of
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
{400, error_msg('ALREADY_EXISTS', <<"bridge already exists">>)};
|
?BAD_REQUEST('ALREADY_EXISTS', <<"bridge already exists">>);
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
Conf = filter_out_request_body(Conf0),
|
Conf = filter_out_request_body(Conf0),
|
||||||
{ok, _} = emqx_bridge:create(BridgeType, BridgeName, Conf),
|
create_bridge(BridgeType, BridgeName, Conf)
|
||||||
lookup_from_all_nodes(BridgeType, BridgeName, 201)
|
|
||||||
end;
|
end;
|
||||||
'/bridges'(get, _Params) ->
|
'/bridges'(get, _Params) ->
|
||||||
Nodes = mria:running_nodes(),
|
Nodes = mria:running_nodes(),
|
||||||
|
@ -478,9 +492,9 @@ schema("/bridges_probe") ->
|
||||||
[format_resource(Data, Node) || Data <- Bridges]
|
[format_resource(Data, Node) || Data <- Bridges]
|
||||||
|| {Node, Bridges} <- lists:zip(Nodes, NodeBridges)
|
|| {Node, Bridges} <- lists:zip(Nodes, NodeBridges)
|
||||||
],
|
],
|
||||||
{200, zip_bridges(AllBridges)};
|
?OK(zip_bridges(AllBridges));
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
{500, error_msg('INTERNAL_ERROR', Reason)}
|
?INTERNAL_ERROR(Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
'/bridges/:id'(get, #{bindings := #{id := Id}}) ->
|
'/bridges/:id'(get, #{bindings := #{id := Id}}) ->
|
||||||
|
@ -493,8 +507,7 @@ schema("/bridges_probe") ->
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
RawConf = emqx:get_raw_config([bridges, BridgeType, BridgeName], #{}),
|
RawConf = emqx:get_raw_config([bridges, BridgeType, BridgeName], #{}),
|
||||||
Conf = deobfuscate(Conf1, RawConf),
|
Conf = deobfuscate(Conf1, RawConf),
|
||||||
{ok, _} = emqx_bridge:create(BridgeType, BridgeName, Conf),
|
update_bridge(BridgeType, BridgeName, Conf);
|
||||||
lookup_from_all_nodes(BridgeType, BridgeName, 200);
|
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
?BRIDGE_NOT_FOUND(BridgeType, BridgeName)
|
?BRIDGE_NOT_FOUND(BridgeType, BridgeName)
|
||||||
end
|
end
|
||||||
|
@ -512,16 +525,16 @@ schema("/bridges_probe") ->
|
||||||
end,
|
end,
|
||||||
case emqx_bridge:check_deps_and_remove(BridgeType, BridgeName, AlsoDeleteActs) of
|
case emqx_bridge:check_deps_and_remove(BridgeType, BridgeName, AlsoDeleteActs) of
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
204;
|
?NO_CONTENT;
|
||||||
{error, {rules_deps_on_this_bridge, RuleIds}} ->
|
{error, {rules_deps_on_this_bridge, RuleIds}} ->
|
||||||
?BAD_REQUEST(
|
?BAD_REQUEST(
|
||||||
{<<"Cannot delete bridge while active rules are defined for this bridge">>,
|
{<<"Cannot delete bridge while active rules are defined for this bridge">>,
|
||||||
RuleIds}
|
RuleIds}
|
||||||
);
|
);
|
||||||
{error, timeout} ->
|
{error, timeout} ->
|
||||||
{503, error_msg('SERVICE_UNAVAILABLE', <<"request timeout">>)};
|
?SERVICE_UNAVAILABLE(<<"request timeout">>);
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
{500, error_msg('INTERNAL_ERROR', Reason)}
|
?INTERNAL_ERROR(Reason)
|
||||||
end;
|
end;
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
?BRIDGE_NOT_FOUND(BridgeType, BridgeName)
|
?BRIDGE_NOT_FOUND(BridgeType, BridgeName)
|
||||||
|
@ -538,7 +551,7 @@ schema("/bridges_probe") ->
|
||||||
ok = emqx_bridge_resource:reset_metrics(
|
ok = emqx_bridge_resource:reset_metrics(
|
||||||
emqx_bridge_resource:resource_id(BridgeType, BridgeName)
|
emqx_bridge_resource:resource_id(BridgeType, BridgeName)
|
||||||
),
|
),
|
||||||
{204}
|
?NO_CONTENT
|
||||||
end
|
end
|
||||||
).
|
).
|
||||||
|
|
||||||
|
@ -549,9 +562,9 @@ schema("/bridges_probe") ->
|
||||||
Params1 = maybe_deobfuscate_bridge_probe(Params),
|
Params1 = maybe_deobfuscate_bridge_probe(Params),
|
||||||
case emqx_bridge_resource:create_dry_run(ConnType, maps:remove(<<"type">>, Params1)) of
|
case emqx_bridge_resource:create_dry_run(ConnType, maps:remove(<<"type">>, Params1)) of
|
||||||
ok ->
|
ok ->
|
||||||
204;
|
?NO_CONTENT;
|
||||||
{error, Reason} when not is_tuple(Reason); element(1, Reason) =/= 'exit' ->
|
{error, Reason} when not is_tuple(Reason); element(1, Reason) =/= 'exit' ->
|
||||||
{400, error_msg('TEST_FAILED', to_hr_reason(Reason))}
|
?BAD_REQUEST('TEST_FAILED', Reason)
|
||||||
end;
|
end;
|
||||||
BadRequest ->
|
BadRequest ->
|
||||||
BadRequest
|
BadRequest
|
||||||
|
@ -585,7 +598,7 @@ do_lookup_from_all_nodes(BridgeType, BridgeName, SuccCode, FormatFun) ->
|
||||||
{ok, [{error, not_found} | _]} ->
|
{ok, [{error, not_found} | _]} ->
|
||||||
?BRIDGE_NOT_FOUND(BridgeType, BridgeName);
|
?BRIDGE_NOT_FOUND(BridgeType, BridgeName);
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
{500, error_msg('INTERNAL_ERROR', Reason)}
|
?INTERNAL_ERROR(Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
lookup_from_local_node(BridgeType, BridgeName) ->
|
lookup_from_local_node(BridgeType, BridgeName) ->
|
||||||
|
@ -594,6 +607,20 @@ lookup_from_local_node(BridgeType, BridgeName) ->
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
create_bridge(BridgeType, BridgeName, Conf) ->
|
||||||
|
create_or_update_bridge(BridgeType, BridgeName, Conf, 201).
|
||||||
|
|
||||||
|
update_bridge(BridgeType, BridgeName, Conf) ->
|
||||||
|
create_or_update_bridge(BridgeType, BridgeName, Conf, 200).
|
||||||
|
|
||||||
|
create_or_update_bridge(BridgeType, BridgeName, Conf, HttpStatusCode) ->
|
||||||
|
case emqx_bridge:create(BridgeType, BridgeName, Conf) of
|
||||||
|
{ok, _} ->
|
||||||
|
lookup_from_all_nodes(BridgeType, BridgeName, HttpStatusCode);
|
||||||
|
{error, #{kind := validation_error} = Reason} ->
|
||||||
|
?BAD_REQUEST(map_to_json(Reason))
|
||||||
|
end.
|
||||||
|
|
||||||
'/bridges/:id/enable/:enable'(put, #{bindings := #{id := Id, enable := Enable}}) ->
|
'/bridges/:id/enable/:enable'(put, #{bindings := #{id := Id, enable := Enable}}) ->
|
||||||
?TRY_PARSE_ID(
|
?TRY_PARSE_ID(
|
||||||
Id,
|
Id,
|
||||||
|
@ -603,15 +630,15 @@ lookup_from_local_node(BridgeType, BridgeName) ->
|
||||||
OperFunc ->
|
OperFunc ->
|
||||||
case emqx_bridge:disable_enable(OperFunc, BridgeType, BridgeName) of
|
case emqx_bridge:disable_enable(OperFunc, BridgeType, BridgeName) of
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
204;
|
?NO_CONTENT;
|
||||||
{error, {pre_config_update, _, bridge_not_found}} ->
|
{error, {pre_config_update, _, bridge_not_found}} ->
|
||||||
?BRIDGE_NOT_FOUND(BridgeType, BridgeName);
|
?BRIDGE_NOT_FOUND(BridgeType, BridgeName);
|
||||||
{error, {_, _, timeout}} ->
|
{error, {_, _, timeout}} ->
|
||||||
{503, error_msg('SERVICE_UNAVAILABLE', <<"request timeout">>)};
|
?SERVICE_UNAVAILABLE(<<"request timeout">>);
|
||||||
{error, timeout} ->
|
{error, timeout} ->
|
||||||
{503, error_msg('SERVICE_UNAVAILABLE', <<"request timeout">>)};
|
?SERVICE_UNAVAILABLE(<<"request timeout">>);
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
{500, error_msg('INTERNAL_ERROR', Reason)}
|
?INTERNAL_ERROR(Reason)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
).
|
).
|
||||||
|
@ -731,7 +758,7 @@ pick_bridges_by_id(Type, Name, BridgesAllNodes) ->
|
||||||
|
|
||||||
format_bridge_info([FirstBridge | _] = Bridges) ->
|
format_bridge_info([FirstBridge | _] = Bridges) ->
|
||||||
Res = maps:without([node, metrics], FirstBridge),
|
Res = maps:without([node, metrics], FirstBridge),
|
||||||
NodeStatus = collect_status(Bridges),
|
NodeStatus = node_status(Bridges),
|
||||||
redact(Res#{
|
redact(Res#{
|
||||||
status => aggregate_status(NodeStatus),
|
status => aggregate_status(NodeStatus),
|
||||||
node_status => NodeStatus
|
node_status => NodeStatus
|
||||||
|
@ -744,8 +771,8 @@ format_bridge_metrics(Bridges) ->
|
||||||
node_metrics => NodeMetrics
|
node_metrics => NodeMetrics
|
||||||
}.
|
}.
|
||||||
|
|
||||||
collect_status(Bridges) ->
|
node_status(Bridges) ->
|
||||||
[maps:with([node, status], B) || B <- Bridges].
|
[maps:with([node, status, status_reason], B) || B <- Bridges].
|
||||||
|
|
||||||
aggregate_status(AllStatus) ->
|
aggregate_status(AllStatus) ->
|
||||||
Head = fun([A | _]) -> A end,
|
Head = fun([A | _]) -> A end,
|
||||||
|
@ -816,52 +843,63 @@ format_resource(
|
||||||
)
|
)
|
||||||
).
|
).
|
||||||
|
|
||||||
format_resource_data(#{status := Status, metrics := Metrics}) ->
|
format_resource_data(ResData) ->
|
||||||
#{status => Status, metrics => format_metrics(Metrics)};
|
maps:fold(fun format_resource_data/3, #{}, maps:with([status, metrics, error], ResData)).
|
||||||
format_resource_data(#{status := Status}) ->
|
|
||||||
#{status => Status}.
|
|
||||||
|
|
||||||
format_metrics(#{
|
format_resource_data(error, undefined, Result) ->
|
||||||
counters := #{
|
Result;
|
||||||
'dropped' := Dropped,
|
format_resource_data(error, Error, Result) ->
|
||||||
'dropped.other' := DroppedOther,
|
Result#{status_reason => emqx_misc:readable_error_msg(Error)};
|
||||||
'dropped.expired' := DroppedExpired,
|
format_resource_data(
|
||||||
'dropped.queue_full' := DroppedQueueFull,
|
metrics,
|
||||||
'dropped.resource_not_found' := DroppedResourceNotFound,
|
#{
|
||||||
'dropped.resource_stopped' := DroppedResourceStopped,
|
counters := #{
|
||||||
'matched' := Matched,
|
'dropped' := Dropped,
|
||||||
'retried' := Retried,
|
'dropped.other' := DroppedOther,
|
||||||
'late_reply' := LateReply,
|
'dropped.expired' := DroppedExpired,
|
||||||
'failed' := SentFailed,
|
'dropped.queue_full' := DroppedQueueFull,
|
||||||
'success' := SentSucc,
|
'dropped.resource_not_found' := DroppedResourceNotFound,
|
||||||
'received' := Rcvd
|
'dropped.resource_stopped' := DroppedResourceStopped,
|
||||||
|
'matched' := Matched,
|
||||||
|
'retried' := Retried,
|
||||||
|
'late_reply' := LateReply,
|
||||||
|
'failed' := SentFailed,
|
||||||
|
'success' := SentSucc,
|
||||||
|
'received' := Rcvd
|
||||||
|
},
|
||||||
|
gauges := Gauges,
|
||||||
|
rate := #{
|
||||||
|
matched := #{current := Rate, last5m := Rate5m, max := RateMax}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
gauges := Gauges,
|
Result
|
||||||
rate := #{
|
) ->
|
||||||
matched := #{current := Rate, last5m := Rate5m, max := RateMax}
|
|
||||||
}
|
|
||||||
}) ->
|
|
||||||
Queued = maps:get('queuing', Gauges, 0),
|
Queued = maps:get('queuing', Gauges, 0),
|
||||||
SentInflight = maps:get('inflight', Gauges, 0),
|
SentInflight = maps:get('inflight', Gauges, 0),
|
||||||
?METRICS(
|
Result#{
|
||||||
Dropped,
|
metrics =>
|
||||||
DroppedOther,
|
?METRICS(
|
||||||
DroppedExpired,
|
Dropped,
|
||||||
DroppedQueueFull,
|
DroppedOther,
|
||||||
DroppedResourceNotFound,
|
DroppedExpired,
|
||||||
DroppedResourceStopped,
|
DroppedQueueFull,
|
||||||
Matched,
|
DroppedResourceNotFound,
|
||||||
Queued,
|
DroppedResourceStopped,
|
||||||
Retried,
|
Matched,
|
||||||
LateReply,
|
Queued,
|
||||||
SentFailed,
|
Retried,
|
||||||
SentInflight,
|
LateReply,
|
||||||
SentSucc,
|
SentFailed,
|
||||||
Rate,
|
SentInflight,
|
||||||
Rate5m,
|
SentSucc,
|
||||||
RateMax,
|
Rate,
|
||||||
Rcvd
|
Rate5m,
|
||||||
).
|
RateMax,
|
||||||
|
Rcvd
|
||||||
|
)
|
||||||
|
};
|
||||||
|
format_resource_data(K, V, Result) ->
|
||||||
|
Result#{K => V}.
|
||||||
|
|
||||||
fill_defaults(Type, RawConf) ->
|
fill_defaults(Type, RawConf) ->
|
||||||
PackedConf = pack_bridge_conf(Type, RawConf),
|
PackedConf = pack_bridge_conf(Type, RawConf),
|
||||||
|
@ -903,6 +941,7 @@ filter_out_request_body(Conf) ->
|
||||||
<<"type">>,
|
<<"type">>,
|
||||||
<<"name">>,
|
<<"name">>,
|
||||||
<<"status">>,
|
<<"status">>,
|
||||||
|
<<"status_reason">>,
|
||||||
<<"node_status">>,
|
<<"node_status">>,
|
||||||
<<"node_metrics">>,
|
<<"node_metrics">>,
|
||||||
<<"metrics">>,
|
<<"metrics">>,
|
||||||
|
@ -910,9 +949,6 @@ filter_out_request_body(Conf) ->
|
||||||
],
|
],
|
||||||
maps:without(ExtraConfs, Conf).
|
maps:without(ExtraConfs, Conf).
|
||||||
|
|
||||||
error_msg(Code, Msg) ->
|
|
||||||
#{code => Code, message => emqx_misc:readable_error_msg(Msg)}.
|
|
||||||
|
|
||||||
bin(S) when is_list(S) ->
|
bin(S) when is_list(S) ->
|
||||||
list_to_binary(S);
|
list_to_binary(S);
|
||||||
bin(S) when is_atom(S) ->
|
bin(S) when is_atom(S) ->
|
||||||
|
@ -923,30 +959,31 @@ bin(S) when is_binary(S) ->
|
||||||
call_operation(NodeOrAll, OperFunc, Args = [_Nodes, BridgeType, BridgeName]) ->
|
call_operation(NodeOrAll, OperFunc, Args = [_Nodes, BridgeType, BridgeName]) ->
|
||||||
case is_ok(do_bpapi_call(NodeOrAll, OperFunc, Args)) of
|
case is_ok(do_bpapi_call(NodeOrAll, OperFunc, Args)) of
|
||||||
Ok when Ok =:= ok; is_tuple(Ok), element(1, Ok) =:= ok ->
|
Ok when Ok =:= ok; is_tuple(Ok), element(1, Ok) =:= ok ->
|
||||||
204;
|
?NO_CONTENT;
|
||||||
{error, not_implemented} ->
|
{error, not_implemented} ->
|
||||||
%% Should only happen if we call `start` on a node that is
|
%% Should only happen if we call `start` on a node that is
|
||||||
%% still on an older bpapi version that doesn't support it.
|
%% still on an older bpapi version that doesn't support it.
|
||||||
maybe_try_restart(NodeOrAll, OperFunc, Args);
|
maybe_try_restart(NodeOrAll, OperFunc, Args);
|
||||||
{error, timeout} ->
|
{error, timeout} ->
|
||||||
{503, error_msg('SERVICE_UNAVAILABLE', <<"request timeout">>)};
|
?SERVICE_UNAVAILABLE(<<"Request timeout">>);
|
||||||
{error, {start_pool_failed, Name, Reason}} ->
|
{error, {start_pool_failed, Name, Reason}} ->
|
||||||
{503,
|
?SERVICE_UNAVAILABLE(
|
||||||
error_msg(
|
bin(io_lib:format("Failed to start ~p pool for reason ~p", [Name, Reason]))
|
||||||
'SERVICE_UNAVAILABLE',
|
);
|
||||||
bin(
|
|
||||||
io_lib:format(
|
|
||||||
"failed to start ~p pool for reason ~p",
|
|
||||||
[Name, Reason]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)};
|
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
?BRIDGE_NOT_FOUND(BridgeType, BridgeName);
|
BridgeId = emqx_bridge_resource:bridge_id(BridgeType, BridgeName),
|
||||||
|
?SLOG(warning, #{
|
||||||
|
msg => "bridge_inconsistent_in_cluster_for_call_operation",
|
||||||
|
reason => not_found,
|
||||||
|
type => BridgeType,
|
||||||
|
name => BridgeName,
|
||||||
|
bridge => BridgeId
|
||||||
|
}),
|
||||||
|
?SERVICE_UNAVAILABLE(<<"Bridge not found on remote node: ", BridgeId/binary>>);
|
||||||
{error, {node_not_found, Node}} ->
|
{error, {node_not_found, Node}} ->
|
||||||
?NOT_FOUND(<<"Node not found: ", (atom_to_binary(Node))/binary>>);
|
?NOT_FOUND(<<"Node not found: ", (atom_to_binary(Node))/binary>>);
|
||||||
{error, Reason} when not is_tuple(Reason); element(1, Reason) =/= 'exit' ->
|
{error, Reason} when not is_tuple(Reason); element(1, Reason) =/= 'exit' ->
|
||||||
?BAD_REQUEST(to_hr_reason(Reason))
|
?BAD_REQUEST(Reason)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
maybe_try_restart(all, start_bridges_to_all_nodes, Args) ->
|
maybe_try_restart(all, start_bridges_to_all_nodes, Args) ->
|
||||||
|
@ -954,7 +991,7 @@ maybe_try_restart(all, start_bridges_to_all_nodes, Args) ->
|
||||||
maybe_try_restart(Node, start_bridge_to_node, Args) ->
|
maybe_try_restart(Node, start_bridge_to_node, Args) ->
|
||||||
call_operation(Node, restart_bridge_to_node, Args);
|
call_operation(Node, restart_bridge_to_node, Args);
|
||||||
maybe_try_restart(_, _, _) ->
|
maybe_try_restart(_, _, _) ->
|
||||||
501.
|
?NOT_IMPLEMENTED.
|
||||||
|
|
||||||
do_bpapi_call(all, Call, Args) ->
|
do_bpapi_call(all, Call, Args) ->
|
||||||
maybe_unwrap(
|
maybe_unwrap(
|
||||||
|
@ -985,19 +1022,6 @@ supported_versions(start_bridge_to_node) -> [2, 3];
|
||||||
supported_versions(start_bridges_to_all_nodes) -> [2, 3];
|
supported_versions(start_bridges_to_all_nodes) -> [2, 3];
|
||||||
supported_versions(_Call) -> [1, 2, 3].
|
supported_versions(_Call) -> [1, 2, 3].
|
||||||
|
|
||||||
to_hr_reason(nxdomain) ->
|
|
||||||
<<"Host not found">>;
|
|
||||||
to_hr_reason(econnrefused) ->
|
|
||||||
<<"Connection refused">>;
|
|
||||||
to_hr_reason({unauthorized_client, _}) ->
|
|
||||||
<<"Unauthorized client">>;
|
|
||||||
to_hr_reason({not_authorized, _}) ->
|
|
||||||
<<"Not authorized">>;
|
|
||||||
to_hr_reason({malformed_username_or_password, _}) ->
|
|
||||||
<<"Malformed username or password">>;
|
|
||||||
to_hr_reason(Reason) ->
|
|
||||||
Reason.
|
|
||||||
|
|
||||||
redact(Term) ->
|
redact(Term) ->
|
||||||
emqx_misc:redact(Term).
|
emqx_misc:redact(Term).
|
||||||
|
|
||||||
|
@ -1021,3 +1045,8 @@ deobfuscate(NewConf, OldConf) ->
|
||||||
#{},
|
#{},
|
||||||
NewConf
|
NewConf
|
||||||
).
|
).
|
||||||
|
|
||||||
|
map_to_json(M) ->
|
||||||
|
emqx_json:encode(
|
||||||
|
emqx_map_lib:jsonable_map(M, fun(K, V) -> {K, emqx_map_lib:binary_string(V)} end)
|
||||||
|
).
|
||||||
|
|
|
@ -106,6 +106,12 @@ common_bridge_fields() ->
|
||||||
status_fields() ->
|
status_fields() ->
|
||||||
[
|
[
|
||||||
{"status", mk(status(), #{desc => ?DESC("desc_status")})},
|
{"status", mk(status(), #{desc => ?DESC("desc_status")})},
|
||||||
|
{"status_reason",
|
||||||
|
mk(binary(), #{
|
||||||
|
required => false,
|
||||||
|
desc => ?DESC("desc_status_reason"),
|
||||||
|
example => <<"Connection refused">>
|
||||||
|
})},
|
||||||
{"node_status",
|
{"node_status",
|
||||||
mk(
|
mk(
|
||||||
hoconsc:array(ref(?MODULE, "node_status")),
|
hoconsc:array(ref(?MODULE, "node_status")),
|
||||||
|
@ -190,7 +196,13 @@ fields("node_metrics") ->
|
||||||
fields("node_status") ->
|
fields("node_status") ->
|
||||||
[
|
[
|
||||||
node_name(),
|
node_name(),
|
||||||
{"status", mk(status(), #{})}
|
{"status", mk(status(), #{})},
|
||||||
|
{"status_reason",
|
||||||
|
mk(binary(), #{
|
||||||
|
required => false,
|
||||||
|
desc => ?DESC("desc_status_reason"),
|
||||||
|
example => <<"Connection refused">>
|
||||||
|
})}
|
||||||
].
|
].
|
||||||
|
|
||||||
desc(bridges) ->
|
desc(bridges) ->
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
-include_lib("common_test/include/ct.hrl").
|
-include_lib("common_test/include/ct.hrl").
|
||||||
-define(CONF_DEFAULT, <<"bridges: {}">>).
|
-define(CONF_DEFAULT, <<"bridges: {}">>).
|
||||||
-define(BRIDGE_TYPE, <<"webhook">>).
|
-define(BRIDGE_TYPE_HTTP, <<"webhook">>).
|
||||||
-define(BRIDGE_NAME, (atom_to_binary(?FUNCTION_NAME))).
|
-define(BRIDGE_NAME, (atom_to_binary(?FUNCTION_NAME))).
|
||||||
-define(URL(PORT, PATH),
|
-define(URL(PORT, PATH),
|
||||||
list_to_binary(
|
list_to_binary(
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
}).
|
}).
|
||||||
-define(MQTT_BRIDGE(SERVER), ?MQTT_BRIDGE(SERVER, <<"mqtt_egress_test_bridge">>)).
|
-define(MQTT_BRIDGE(SERVER), ?MQTT_BRIDGE(SERVER, <<"mqtt_egress_test_bridge">>)).
|
||||||
|
|
||||||
-define(HTTP_BRIDGE(URL, TYPE, NAME), ?BRIDGE(NAME, TYPE)#{
|
-define(HTTP_BRIDGE(URL, NAME), ?BRIDGE(NAME, ?BRIDGE_TYPE_HTTP)#{
|
||||||
<<"url">> => URL,
|
<<"url">> => URL,
|
||||||
<<"local_topic">> => <<"emqx_webhook/#">>,
|
<<"local_topic">> => <<"emqx_webhook/#">>,
|
||||||
<<"method">> => <<"post">>,
|
<<"method">> => <<"post">>,
|
||||||
|
@ -57,6 +57,7 @@
|
||||||
<<"content-type">> => <<"application/json">>
|
<<"content-type">> => <<"application/json">>
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
|
-define(HTTP_BRIDGE(URL), ?HTTP_BRIDGE(URL, ?BRIDGE_NAME)).
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
@ -97,6 +98,20 @@ init_per_testcase(t_old_bpapi_vsn, Config) ->
|
||||||
meck:expect(emqx_bpapi, supported_version, 1, 1),
|
meck:expect(emqx_bpapi, supported_version, 1, 1),
|
||||||
meck:expect(emqx_bpapi, supported_version, 2, 1),
|
meck:expect(emqx_bpapi, supported_version, 2, 1),
|
||||||
init_per_testcase(common, Config);
|
init_per_testcase(common, Config);
|
||||||
|
init_per_testcase(StartStop, Config) when
|
||||||
|
StartStop == t_start_stop_bridges_cluster;
|
||||||
|
StartStop == t_start_stop_bridges_node
|
||||||
|
->
|
||||||
|
meck:new(emqx_bridge_resource, [passthrough]),
|
||||||
|
meck:expect(
|
||||||
|
emqx_bridge_resource,
|
||||||
|
stop,
|
||||||
|
fun
|
||||||
|
(_, <<"bridge_not_found">>) -> {error, not_found};
|
||||||
|
(Type, Name) -> meck:passthrough([Type, Name])
|
||||||
|
end
|
||||||
|
),
|
||||||
|
init_per_testcase(common, Config);
|
||||||
init_per_testcase(_, Config) ->
|
init_per_testcase(_, Config) ->
|
||||||
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
{ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000),
|
||||||
{Port, Sock, Acceptor} = start_http_server(fun handle_fun_200_ok/2),
|
{Port, Sock, Acceptor} = start_http_server(fun handle_fun_200_ok/2),
|
||||||
|
@ -108,6 +123,12 @@ end_per_testcase(t_broken_bpapi_vsn, Config) ->
|
||||||
end_per_testcase(t_old_bpapi_vsn, Config) ->
|
end_per_testcase(t_old_bpapi_vsn, Config) ->
|
||||||
meck:unload([emqx_bpapi]),
|
meck:unload([emqx_bpapi]),
|
||||||
end_per_testcase(common, Config);
|
end_per_testcase(common, Config);
|
||||||
|
end_per_testcase(StartStop, Config) when
|
||||||
|
StartStop == t_start_stop_bridges_cluster;
|
||||||
|
StartStop == t_start_stop_bridges_node
|
||||||
|
->
|
||||||
|
meck:unload([emqx_bridge_resource]),
|
||||||
|
end_per_testcase(common, Config);
|
||||||
end_per_testcase(_, Config) ->
|
end_per_testcase(_, Config) ->
|
||||||
Sock = ?config(sock, Config),
|
Sock = ?config(sock, Config),
|
||||||
Acceptor = ?config(acceptor, Config),
|
Acceptor = ?config(acceptor, Config),
|
||||||
|
@ -206,12 +227,12 @@ t_http_crud_apis(Config) ->
|
||||||
{ok, 201, Bridge} = request(
|
{ok, 201, Bridge} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges"]),
|
uri(["bridges"]),
|
||||||
?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name)
|
?HTTP_BRIDGE(URL1, Name)
|
||||||
),
|
),
|
||||||
|
|
||||||
%ct:pal("---bridge: ~p", [Bridge]),
|
%ct:pal("---bridge: ~p", [Bridge]),
|
||||||
#{
|
#{
|
||||||
<<"type">> := ?BRIDGE_TYPE,
|
<<"type">> := ?BRIDGE_TYPE_HTTP,
|
||||||
<<"name">> := Name,
|
<<"name">> := Name,
|
||||||
<<"enable">> := true,
|
<<"enable">> := true,
|
||||||
<<"status">> := _,
|
<<"status">> := _,
|
||||||
|
@ -219,7 +240,7 @@ t_http_crud_apis(Config) ->
|
||||||
<<"url">> := URL1
|
<<"url">> := URL1
|
||||||
} = emqx_json:decode(Bridge, [return_maps]),
|
} = emqx_json:decode(Bridge, [return_maps]),
|
||||||
|
|
||||||
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name),
|
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE_HTTP, Name),
|
||||||
%% send an message to emqx and the message should be forwarded to the HTTP server
|
%% send an message to emqx and the message should be forwarded to the HTTP server
|
||||||
Body = <<"my msg">>,
|
Body = <<"my msg">>,
|
||||||
emqx:publish(emqx_message:make(<<"emqx_webhook/1">>, Body)),
|
emqx:publish(emqx_message:make(<<"emqx_webhook/1">>, Body)),
|
||||||
|
@ -243,11 +264,11 @@ t_http_crud_apis(Config) ->
|
||||||
{ok, 200, Bridge2} = request(
|
{ok, 200, Bridge2} = request(
|
||||||
put,
|
put,
|
||||||
uri(["bridges", BridgeID]),
|
uri(["bridges", BridgeID]),
|
||||||
?HTTP_BRIDGE(URL2, ?BRIDGE_TYPE, Name)
|
?HTTP_BRIDGE(URL2, Name)
|
||||||
),
|
),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
#{
|
#{
|
||||||
<<"type">> := ?BRIDGE_TYPE,
|
<<"type">> := ?BRIDGE_TYPE_HTTP,
|
||||||
<<"name">> := Name,
|
<<"name">> := Name,
|
||||||
<<"enable">> := true,
|
<<"enable">> := true,
|
||||||
<<"status">> := _,
|
<<"status">> := _,
|
||||||
|
@ -262,7 +283,7 @@ t_http_crud_apis(Config) ->
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
[
|
[
|
||||||
#{
|
#{
|
||||||
<<"type">> := ?BRIDGE_TYPE,
|
<<"type">> := ?BRIDGE_TYPE_HTTP,
|
||||||
<<"name">> := Name,
|
<<"name">> := Name,
|
||||||
<<"enable">> := true,
|
<<"enable">> := true,
|
||||||
<<"status">> := _,
|
<<"status">> := _,
|
||||||
|
@ -277,7 +298,7 @@ t_http_crud_apis(Config) ->
|
||||||
{ok, 200, Bridge3Str} = request(get, uri(["bridges", BridgeID]), []),
|
{ok, 200, Bridge3Str} = request(get, uri(["bridges", BridgeID]), []),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
#{
|
#{
|
||||||
<<"type">> := ?BRIDGE_TYPE,
|
<<"type">> := ?BRIDGE_TYPE_HTTP,
|
||||||
<<"name">> := Name,
|
<<"name">> := Name,
|
||||||
<<"enable">> := true,
|
<<"enable">> := true,
|
||||||
<<"status">> := _,
|
<<"status">> := _,
|
||||||
|
@ -301,6 +322,33 @@ t_http_crud_apis(Config) ->
|
||||||
end
|
end
|
||||||
),
|
),
|
||||||
|
|
||||||
|
%% Test bad updates
|
||||||
|
{ok, 400, PutFail1} = request(
|
||||||
|
put,
|
||||||
|
uri(["bridges", BridgeID]),
|
||||||
|
maps:remove(<<"url">>, ?HTTP_BRIDGE(URL2, Name))
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
#{<<"reason">> := <<"required_field">>},
|
||||||
|
emqx_json:decode(maps:get(<<"message">>, emqx_json:decode(PutFail1, [return_maps])), [
|
||||||
|
return_maps
|
||||||
|
])
|
||||||
|
),
|
||||||
|
{ok, 400, PutFail2} = request(
|
||||||
|
put,
|
||||||
|
uri(["bridges", BridgeID]),
|
||||||
|
maps:put(<<"curl">>, URL2, maps:remove(<<"url">>, ?HTTP_BRIDGE(URL2, Name)))
|
||||||
|
),
|
||||||
|
?assertMatch(
|
||||||
|
#{
|
||||||
|
<<"reason">> := <<"unknown_fields">>,
|
||||||
|
<<"unknown">> := <<"curl">>
|
||||||
|
},
|
||||||
|
emqx_json:decode(maps:get(<<"message">>, emqx_json:decode(PutFail2, [return_maps])), [
|
||||||
|
return_maps
|
||||||
|
])
|
||||||
|
),
|
||||||
|
|
||||||
%% delete the bridge
|
%% delete the bridge
|
||||||
{ok, 204, <<>>} = request(delete, uri(["bridges", BridgeID]), []),
|
{ok, 204, <<>>} = request(delete, uri(["bridges", BridgeID]), []),
|
||||||
{ok, 200, <<"[]">>} = request(get, uri(["bridges"]), []),
|
{ok, 200, <<"[]">>} = request(get, uri(["bridges"]), []),
|
||||||
|
@ -309,7 +357,7 @@ t_http_crud_apis(Config) ->
|
||||||
{ok, 404, ErrMsg2} = request(
|
{ok, 404, ErrMsg2} = request(
|
||||||
put,
|
put,
|
||||||
uri(["bridges", BridgeID]),
|
uri(["bridges", BridgeID]),
|
||||||
?HTTP_BRIDGE(URL2, ?BRIDGE_TYPE, Name)
|
?HTTP_BRIDGE(URL2, Name)
|
||||||
),
|
),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
#{
|
#{
|
||||||
|
@ -338,6 +386,37 @@ t_http_crud_apis(Config) ->
|
||||||
},
|
},
|
||||||
emqx_json:decode(ErrMsg3, [return_maps])
|
emqx_json:decode(ErrMsg3, [return_maps])
|
||||||
),
|
),
|
||||||
|
|
||||||
|
%% Create non working bridge
|
||||||
|
BrokenURL = ?URL(Port + 1, "/foo"),
|
||||||
|
{ok, 201, BrokenBridge} = request(
|
||||||
|
post,
|
||||||
|
uri(["bridges"]),
|
||||||
|
?HTTP_BRIDGE(BrokenURL, Name)
|
||||||
|
),
|
||||||
|
#{
|
||||||
|
<<"type">> := ?BRIDGE_TYPE_HTTP,
|
||||||
|
<<"name">> := Name,
|
||||||
|
<<"enable">> := true,
|
||||||
|
<<"status">> := <<"disconnected">>,
|
||||||
|
<<"status_reason">> := <<"Connection refused">>,
|
||||||
|
<<"node_status">> := [
|
||||||
|
#{<<"status">> := <<"disconnected">>, <<"status_reason">> := <<"Connection refused">>}
|
||||||
|
| _
|
||||||
|
],
|
||||||
|
<<"url">> := BrokenURL
|
||||||
|
} = emqx_json:decode(BrokenBridge, [return_maps]),
|
||||||
|
{ok, 200, FixedBridgeResponse} = request(put, uri(["bridges", BridgeID]), ?HTTP_BRIDGE(URL1)),
|
||||||
|
#{
|
||||||
|
<<"status">> := <<"connected">>,
|
||||||
|
<<"node_status">> := [FixedNodeStatus = #{<<"status">> := <<"connected">>} | _]
|
||||||
|
} = FixedBridge = emqx_json:decode(FixedBridgeResponse, [return_maps]),
|
||||||
|
?assert(not maps:is_key(<<"status_reason">>, FixedBridge)),
|
||||||
|
?assert(not maps:is_key(<<"status_reason">>, FixedNodeStatus)),
|
||||||
|
{ok, 204, <<>>} = request(delete, uri(["bridges", BridgeID]), []),
|
||||||
|
|
||||||
|
%% Try create bridge with bad characters as name
|
||||||
|
{ok, 400, _} = request(post, uri(["bridges"]), ?HTTP_BRIDGE(URL1, <<"隋达"/utf8>>)),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
t_http_bridges_local_topic(Config) ->
|
t_http_bridges_local_topic(Config) ->
|
||||||
|
@ -354,16 +433,16 @@ t_http_bridges_local_topic(Config) ->
|
||||||
{ok, 201, _} = request(
|
{ok, 201, _} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges"]),
|
uri(["bridges"]),
|
||||||
?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name1)
|
?HTTP_BRIDGE(URL1, Name1)
|
||||||
),
|
),
|
||||||
%% and we create another one without local_topic
|
%% and we create another one without local_topic
|
||||||
{ok, 201, _} = request(
|
{ok, 201, _} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges"]),
|
uri(["bridges"]),
|
||||||
maps:remove(<<"local_topic">>, ?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name2))
|
maps:remove(<<"local_topic">>, ?HTTP_BRIDGE(URL1, Name2))
|
||||||
),
|
),
|
||||||
BridgeID1 = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name1),
|
BridgeID1 = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE_HTTP, Name1),
|
||||||
BridgeID2 = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name2),
|
BridgeID2 = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE_HTTP, Name2),
|
||||||
%% Send an message to emqx and the message should be forwarded to the HTTP server.
|
%% Send an message to emqx and the message should be forwarded to the HTTP server.
|
||||||
%% This is to verify we can have 2 bridges with and without local_topic fields
|
%% This is to verify we can have 2 bridges with and without local_topic fields
|
||||||
%% at the same time.
|
%% at the same time.
|
||||||
|
@ -398,11 +477,11 @@ t_check_dependent_actions_on_delete(Config) ->
|
||||||
%% POST /bridges/ will create a bridge
|
%% POST /bridges/ will create a bridge
|
||||||
URL1 = ?URL(Port, "path1"),
|
URL1 = ?URL(Port, "path1"),
|
||||||
Name = <<"t_http_crud_apis">>,
|
Name = <<"t_http_crud_apis">>,
|
||||||
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name),
|
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE_HTTP, Name),
|
||||||
{ok, 201, _} = request(
|
{ok, 201, _} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges"]),
|
uri(["bridges"]),
|
||||||
?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name)
|
?HTTP_BRIDGE(URL1, Name)
|
||||||
),
|
),
|
||||||
{ok, 201, Rule} = request(
|
{ok, 201, Rule} = request(
|
||||||
post,
|
post,
|
||||||
|
@ -436,11 +515,11 @@ t_cascade_delete_actions(Config) ->
|
||||||
%% POST /bridges/ will create a bridge
|
%% POST /bridges/ will create a bridge
|
||||||
URL1 = ?URL(Port, "path1"),
|
URL1 = ?URL(Port, "path1"),
|
||||||
Name = <<"t_http_crud_apis">>,
|
Name = <<"t_http_crud_apis">>,
|
||||||
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name),
|
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE_HTTP, Name),
|
||||||
{ok, 201, _} = request(
|
{ok, 201, _} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges"]),
|
uri(["bridges"]),
|
||||||
?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name)
|
?HTTP_BRIDGE(URL1, Name)
|
||||||
),
|
),
|
||||||
{ok, 201, Rule} = request(
|
{ok, 201, Rule} = request(
|
||||||
post,
|
post,
|
||||||
|
@ -470,7 +549,7 @@ t_cascade_delete_actions(Config) ->
|
||||||
{ok, 201, _} = request(
|
{ok, 201, _} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges"]),
|
uri(["bridges"]),
|
||||||
?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name)
|
?HTTP_BRIDGE(URL1, Name)
|
||||||
),
|
),
|
||||||
{ok, 201, _} = request(
|
{ok, 201, _} = request(
|
||||||
post,
|
post,
|
||||||
|
@ -494,9 +573,9 @@ t_broken_bpapi_vsn(Config) ->
|
||||||
{ok, 201, _Bridge} = request(
|
{ok, 201, _Bridge} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges"]),
|
uri(["bridges"]),
|
||||||
?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name)
|
?HTTP_BRIDGE(URL1, Name)
|
||||||
),
|
),
|
||||||
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name),
|
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE_HTTP, Name),
|
||||||
%% still works since we redirect to 'restart'
|
%% still works since we redirect to 'restart'
|
||||||
{ok, 501, <<>>} = request(post, operation_path(cluster, start, BridgeID), <<"">>),
|
{ok, 501, <<>>} = request(post, operation_path(cluster, start, BridgeID), <<"">>),
|
||||||
{ok, 501, <<>>} = request(post, operation_path(node, start, BridgeID), <<"">>),
|
{ok, 501, <<>>} = request(post, operation_path(node, start, BridgeID), <<"">>),
|
||||||
|
@ -509,9 +588,9 @@ t_old_bpapi_vsn(Config) ->
|
||||||
{ok, 201, _Bridge} = request(
|
{ok, 201, _Bridge} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges"]),
|
uri(["bridges"]),
|
||||||
?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name)
|
?HTTP_BRIDGE(URL1, Name)
|
||||||
),
|
),
|
||||||
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name),
|
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE_HTTP, Name),
|
||||||
{ok, 204, <<>>} = request(post, operation_path(cluster, stop, BridgeID), <<"">>),
|
{ok, 204, <<>>} = request(post, operation_path(cluster, stop, BridgeID), <<"">>),
|
||||||
{ok, 204, <<>>} = request(post, operation_path(node, stop, BridgeID), <<"">>),
|
{ok, 204, <<>>} = request(post, operation_path(node, stop, BridgeID), <<"">>),
|
||||||
%% still works since we redirect to 'restart'
|
%% still works since we redirect to 'restart'
|
||||||
|
@ -549,18 +628,18 @@ do_start_stop_bridges(Type, Config) ->
|
||||||
{ok, 201, Bridge} = request(
|
{ok, 201, Bridge} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges"]),
|
uri(["bridges"]),
|
||||||
?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name)
|
?HTTP_BRIDGE(URL1, Name)
|
||||||
),
|
),
|
||||||
%ct:pal("the bridge ==== ~p", [Bridge]),
|
%ct:pal("the bridge ==== ~p", [Bridge]),
|
||||||
#{
|
#{
|
||||||
<<"type">> := ?BRIDGE_TYPE,
|
<<"type">> := ?BRIDGE_TYPE_HTTP,
|
||||||
<<"name">> := Name,
|
<<"name">> := Name,
|
||||||
<<"enable">> := true,
|
<<"enable">> := true,
|
||||||
<<"status">> := <<"connected">>,
|
<<"status">> := <<"connected">>,
|
||||||
<<"node_status">> := [_ | _],
|
<<"node_status">> := [_ | _],
|
||||||
<<"url">> := URL1
|
<<"url">> := URL1
|
||||||
} = emqx_json:decode(Bridge, [return_maps]),
|
} = emqx_json:decode(Bridge, [return_maps]),
|
||||||
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name),
|
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE_HTTP, Name),
|
||||||
%% stop it
|
%% stop it
|
||||||
{ok, 204, <<>>} = request(post, operation_path(Type, stop, BridgeID), <<"">>),
|
{ok, 204, <<>>} = request(post, operation_path(Type, stop, BridgeID), <<"">>),
|
||||||
{ok, 200, Bridge2} = request(get, uri(["bridges", BridgeID]), []),
|
{ok, 200, Bridge2} = request(get, uri(["bridges", BridgeID]), []),
|
||||||
|
@ -595,6 +674,16 @@ do_start_stop_bridges(Type, Config) ->
|
||||||
%% Looks ok but doesn't exist
|
%% Looks ok but doesn't exist
|
||||||
{ok, 404, _} = request(post, operation_path(Type, start, <<"webhook:cptn_hook">>), <<"">>),
|
{ok, 404, _} = request(post, operation_path(Type, start, <<"webhook:cptn_hook">>), <<"">>),
|
||||||
|
|
||||||
|
%%
|
||||||
|
{ok, 201, _Bridge} = request(
|
||||||
|
post,
|
||||||
|
uri(["bridges"]),
|
||||||
|
?HTTP_BRIDGE(URL1, <<"bridge_not_found">>)
|
||||||
|
),
|
||||||
|
{ok, 503, _} = request(
|
||||||
|
post, operation_path(Type, stop, <<"webhook:bridge_not_found">>), <<"">>
|
||||||
|
),
|
||||||
|
|
||||||
%% Create broken bridge
|
%% Create broken bridge
|
||||||
{ListenPort, Sock} = listen_on_random_port(),
|
{ListenPort, Sock} = listen_on_random_port(),
|
||||||
%% Connecting to this endpoint should always timeout
|
%% Connecting to this endpoint should always timeout
|
||||||
|
@ -631,18 +720,18 @@ t_enable_disable_bridges(Config) ->
|
||||||
{ok, 201, Bridge} = request(
|
{ok, 201, Bridge} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges"]),
|
uri(["bridges"]),
|
||||||
?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name)
|
?HTTP_BRIDGE(URL1, Name)
|
||||||
),
|
),
|
||||||
%ct:pal("the bridge ==== ~p", [Bridge]),
|
%ct:pal("the bridge ==== ~p", [Bridge]),
|
||||||
#{
|
#{
|
||||||
<<"type">> := ?BRIDGE_TYPE,
|
<<"type">> := ?BRIDGE_TYPE_HTTP,
|
||||||
<<"name">> := Name,
|
<<"name">> := Name,
|
||||||
<<"enable">> := true,
|
<<"enable">> := true,
|
||||||
<<"status">> := <<"connected">>,
|
<<"status">> := <<"connected">>,
|
||||||
<<"node_status">> := [_ | _],
|
<<"node_status">> := [_ | _],
|
||||||
<<"url">> := URL1
|
<<"url">> := URL1
|
||||||
} = emqx_json:decode(Bridge, [return_maps]),
|
} = emqx_json:decode(Bridge, [return_maps]),
|
||||||
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name),
|
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE_HTTP, Name),
|
||||||
%% disable it
|
%% disable it
|
||||||
{ok, 204, <<>>} = request(put, enable_path(false, BridgeID), <<"">>),
|
{ok, 204, <<>>} = request(put, enable_path(false, BridgeID), <<"">>),
|
||||||
{ok, 200, Bridge2} = request(get, uri(["bridges", BridgeID]), []),
|
{ok, 200, Bridge2} = request(get, uri(["bridges", BridgeID]), []),
|
||||||
|
@ -688,18 +777,18 @@ t_reset_bridges(Config) ->
|
||||||
{ok, 201, Bridge} = request(
|
{ok, 201, Bridge} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges"]),
|
uri(["bridges"]),
|
||||||
?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name)
|
?HTTP_BRIDGE(URL1, Name)
|
||||||
),
|
),
|
||||||
%ct:pal("the bridge ==== ~p", [Bridge]),
|
%ct:pal("the bridge ==== ~p", [Bridge]),
|
||||||
#{
|
#{
|
||||||
<<"type">> := ?BRIDGE_TYPE,
|
<<"type">> := ?BRIDGE_TYPE_HTTP,
|
||||||
<<"name">> := Name,
|
<<"name">> := Name,
|
||||||
<<"enable">> := true,
|
<<"enable">> := true,
|
||||||
<<"status">> := <<"connected">>,
|
<<"status">> := <<"connected">>,
|
||||||
<<"node_status">> := [_ | _],
|
<<"node_status">> := [_ | _],
|
||||||
<<"url">> := URL1
|
<<"url">> := URL1
|
||||||
} = emqx_json:decode(Bridge, [return_maps]),
|
} = emqx_json:decode(Bridge, [return_maps]),
|
||||||
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name),
|
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE_HTTP, Name),
|
||||||
{ok, 204, <<>>} = request(put, uri(["bridges", BridgeID, "metrics/reset"]), []),
|
{ok, 204, <<>>} = request(put, uri(["bridges", BridgeID, "metrics/reset"]), []),
|
||||||
|
|
||||||
%% delete the bridge
|
%% delete the bridge
|
||||||
|
@ -746,20 +835,20 @@ t_bridges_probe(Config) ->
|
||||||
{ok, 204, <<>>} = request(
|
{ok, 204, <<>>} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges_probe"]),
|
uri(["bridges_probe"]),
|
||||||
?HTTP_BRIDGE(URL, ?BRIDGE_TYPE, ?BRIDGE_NAME)
|
?HTTP_BRIDGE(URL)
|
||||||
),
|
),
|
||||||
|
|
||||||
%% second time with same name is ok since no real bridge created
|
%% second time with same name is ok since no real bridge created
|
||||||
{ok, 204, <<>>} = request(
|
{ok, 204, <<>>} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges_probe"]),
|
uri(["bridges_probe"]),
|
||||||
?HTTP_BRIDGE(URL, ?BRIDGE_TYPE, ?BRIDGE_NAME)
|
?HTTP_BRIDGE(URL)
|
||||||
),
|
),
|
||||||
|
|
||||||
{ok, 400, NxDomain} = request(
|
{ok, 400, NxDomain} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges_probe"]),
|
uri(["bridges_probe"]),
|
||||||
?HTTP_BRIDGE(<<"http://203.0.113.3:1234/foo">>, ?BRIDGE_TYPE, ?BRIDGE_NAME)
|
?HTTP_BRIDGE(<<"http://203.0.113.3:1234/foo">>)
|
||||||
),
|
),
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
#{
|
#{
|
||||||
|
@ -788,7 +877,7 @@ t_bridges_probe(Config) ->
|
||||||
emqx_json:decode(ConnRefused, [return_maps])
|
emqx_json:decode(ConnRefused, [return_maps])
|
||||||
),
|
),
|
||||||
|
|
||||||
{ok, 400, HostNotFound} = request(
|
{ok, 400, CouldNotResolveHost} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges_probe"]),
|
uri(["bridges_probe"]),
|
||||||
?MQTT_BRIDGE(<<"nohost:2883">>)
|
?MQTT_BRIDGE(<<"nohost:2883">>)
|
||||||
|
@ -796,9 +885,9 @@ t_bridges_probe(Config) ->
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
#{
|
#{
|
||||||
<<"code">> := <<"TEST_FAILED">>,
|
<<"code">> := <<"TEST_FAILED">>,
|
||||||
<<"message">> := <<"Host not found">>
|
<<"message">> := <<"Could not resolve host">>
|
||||||
},
|
},
|
||||||
emqx_json:decode(HostNotFound, [return_maps])
|
emqx_json:decode(CouldNotResolveHost, [return_maps])
|
||||||
),
|
),
|
||||||
|
|
||||||
AuthnConfig = #{
|
AuthnConfig = #{
|
||||||
|
@ -842,7 +931,7 @@ t_bridges_probe(Config) ->
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
#{
|
#{
|
||||||
<<"code">> := <<"TEST_FAILED">>,
|
<<"code">> := <<"TEST_FAILED">>,
|
||||||
<<"message">> := <<"Malformed username or password">>
|
<<"message">> := <<"Bad username or password">>
|
||||||
},
|
},
|
||||||
emqx_json:decode(Malformed, [return_maps])
|
emqx_json:decode(Malformed, [return_maps])
|
||||||
),
|
),
|
||||||
|
@ -880,13 +969,13 @@ t_metrics(Config) ->
|
||||||
{ok, 201, Bridge} = request(
|
{ok, 201, Bridge} = request(
|
||||||
post,
|
post,
|
||||||
uri(["bridges"]),
|
uri(["bridges"]),
|
||||||
?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name)
|
?HTTP_BRIDGE(URL1, Name)
|
||||||
),
|
),
|
||||||
|
|
||||||
%ct:pal("---bridge: ~p", [Bridge]),
|
%ct:pal("---bridge: ~p", [Bridge]),
|
||||||
Decoded = emqx_json:decode(Bridge, [return_maps]),
|
Decoded = emqx_json:decode(Bridge, [return_maps]),
|
||||||
#{
|
#{
|
||||||
<<"type">> := ?BRIDGE_TYPE,
|
<<"type">> := ?BRIDGE_TYPE_HTTP,
|
||||||
<<"name">> := Name,
|
<<"name">> := Name,
|
||||||
<<"enable">> := true,
|
<<"enable">> := true,
|
||||||
<<"status">> := _,
|
<<"status">> := _,
|
||||||
|
@ -898,7 +987,7 @@ t_metrics(Config) ->
|
||||||
?assertNot(maps:is_key(<<"metrics">>, Decoded)),
|
?assertNot(maps:is_key(<<"metrics">>, Decoded)),
|
||||||
?assertNot(maps:is_key(<<"node_metrics">>, Decoded)),
|
?assertNot(maps:is_key(<<"node_metrics">>, Decoded)),
|
||||||
|
|
||||||
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE, Name),
|
BridgeID = emqx_bridge_resource:bridge_id(?BRIDGE_TYPE_HTTP, Name),
|
||||||
|
|
||||||
%% check for empty bridge metrics
|
%% check for empty bridge metrics
|
||||||
{ok, 200, Bridge1Str} = request(get, uri(["bridges", BridgeID, "metrics"]), []),
|
{ok, 200, Bridge1Str} = request(get, uri(["bridges", BridgeID, "metrics"]), []),
|
||||||
|
@ -963,7 +1052,7 @@ t_inconsistent_webhook_request_timeouts(Config) ->
|
||||||
Name = ?BRIDGE_NAME,
|
Name = ?BRIDGE_NAME,
|
||||||
BadBridgeParams =
|
BadBridgeParams =
|
||||||
emqx_map_lib:deep_merge(
|
emqx_map_lib:deep_merge(
|
||||||
?HTTP_BRIDGE(URL1, ?BRIDGE_TYPE, Name),
|
?HTTP_BRIDGE(URL1, Name),
|
||||||
#{
|
#{
|
||||||
<<"request_timeout">> => <<"1s">>,
|
<<"request_timeout">> => <<"1s">>,
|
||||||
<<"resource_opts">> => #{<<"request_timeout">> => <<"2s">>}
|
<<"resource_opts">> => #{<<"request_timeout">> => <<"2s">>}
|
||||||
|
|
|
@ -1,4 +1,41 @@
|
||||||
emqx_ctl
|
# emqx_ctl
|
||||||
=====
|
|
||||||
|
|
||||||
Backend module for `emqx_ctl` command.
|
This application accepts dynamic `emqx ctl` command registrations so plugins can add their own commands.
|
||||||
|
Please note that the 'proxy' command `emqx_ctl` is considered deprecated, going forward, please use `emqx ctl` instead.
|
||||||
|
|
||||||
|
## Add a new command
|
||||||
|
|
||||||
|
To add a new command, the application must implement a callback function to handle the command, and register the command with `emqx_ctl:register_command/2` API.
|
||||||
|
|
||||||
|
### Register
|
||||||
|
|
||||||
|
To add a new command which can be executed from `emqx ctl`, the application must call `emqx_ctl:register_command/2` API to register the command.
|
||||||
|
|
||||||
|
For example, to add a new command `myplugin` which is to be executed as `emqx ctl myplugin`, the application must call `emqx_ctl:register_command/2` API as follows:
|
||||||
|
|
||||||
|
```erlang
|
||||||
|
emqx_ctl:register_command(mypluin, {myplugin_cli, cmd}).
|
||||||
|
```
|
||||||
|
|
||||||
|
### Callback
|
||||||
|
|
||||||
|
The callback function must be exported by the application and must have the following signature:
|
||||||
|
|
||||||
|
```erlang
|
||||||
|
cmd([Arg1, Arg2, ...]) -> ok.
|
||||||
|
```
|
||||||
|
|
||||||
|
It must also implement a special clause to handle the `usage` argument:
|
||||||
|
|
||||||
|
```erlang
|
||||||
|
cmd([usage]) -> "myplugin [arg1] [arg2] ...";
|
||||||
|
```
|
||||||
|
|
||||||
|
### Utility
|
||||||
|
|
||||||
|
The `emqx_ctl` application provides some utility functions which help to format the output of the command.
|
||||||
|
For example `emqx_ctl:print/2` and `emqx_ctl:usage/1`.
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
|
||||||
|
[emqx_management_cli](../emqx_management/src/emqx_mgmt_cli.erl) can be taken as a reference for how to implement a command.
|
||||||
|
|
|
@ -74,7 +74,7 @@ schema("/login") ->
|
||||||
post => #{
|
post => #{
|
||||||
tags => [<<"dashboard">>],
|
tags => [<<"dashboard">>],
|
||||||
desc => ?DESC(login_api),
|
desc => ?DESC(login_api),
|
||||||
summary => <<"Dashboard Auth">>,
|
summary => <<"Dashboard authentication">>,
|
||||||
'requestBody' => fields([username, password]),
|
'requestBody' => fields([username, password]),
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => fields([token, version, license]),
|
200 => fields([token, version, license]),
|
||||||
|
|
|
@ -457,7 +457,18 @@ trans_description(Spec, Hocon) ->
|
||||||
Spec;
|
Spec;
|
||||||
Desc ->
|
Desc ->
|
||||||
Desc1 = binary:replace(Desc, [<<"\n">>], <<"<br/>">>, [global]),
|
Desc1 = binary:replace(Desc, [<<"\n">>], <<"<br/>">>, [global]),
|
||||||
Spec#{description => Desc1}
|
maybe_add_summary_from_label(Spec#{description => Desc1}, Hocon)
|
||||||
|
end.
|
||||||
|
|
||||||
|
maybe_add_summary_from_label(Spec, Hocon) ->
|
||||||
|
Label =
|
||||||
|
case desc_struct(Hocon) of
|
||||||
|
?DESC(_, _) = Struct -> get_i18n(<<"label">>, Struct, undefined);
|
||||||
|
_ -> undefined
|
||||||
|
end,
|
||||||
|
case Label of
|
||||||
|
undefined -> Spec;
|
||||||
|
_ -> Spec#{summary => Label}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_i18n(Key, Struct, Default) ->
|
get_i18n(Key, Struct, Default) ->
|
||||||
|
|
|
@ -56,14 +56,6 @@
|
||||||
all() ->
|
all() ->
|
||||||
emqx_common_test_helpers:all(?MODULE).
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
end_suite() ->
|
|
||||||
end_suite([]).
|
|
||||||
|
|
||||||
end_suite(Apps) ->
|
|
||||||
application:unload(emqx_management),
|
|
||||||
mnesia:clear_table(?ADMIN),
|
|
||||||
emqx_common_test_helpers:stop_apps(Apps ++ [emqx_dashboard]).
|
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
emqx_common_test_helpers:start_apps(
|
emqx_common_test_helpers:start_apps(
|
||||||
[emqx_management, emqx_dashboard],
|
[emqx_management, emqx_dashboard],
|
||||||
|
@ -72,6 +64,7 @@ init_per_suite(Config) ->
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(_Config) ->
|
||||||
|
mnesia:clear_table(?ADMIN),
|
||||||
emqx_common_test_helpers:stop_apps([emqx_dashboard, emqx_management]),
|
emqx_common_test_helpers:stop_apps([emqx_dashboard, emqx_management]),
|
||||||
mria:stop().
|
mria:stop().
|
||||||
|
|
||||||
|
|
|
@ -33,10 +33,12 @@ all() ->
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
application:load(emqx_dashboard),
|
application:load(emqx_dashboard),
|
||||||
mria:start(),
|
mria:start(),
|
||||||
|
mnesia:clear_table(?ADMIN),
|
||||||
emqx_common_test_helpers:start_apps([emqx_dashboard], fun set_special_configs/1),
|
emqx_common_test_helpers:start_apps([emqx_dashboard], fun set_special_configs/1),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
end_per_suite(Config) ->
|
end_per_suite(Config) ->
|
||||||
|
mnesia:clear_table(?ADMIN),
|
||||||
emqx_common_test_helpers:stop_apps([emqx_dashboard]),
|
emqx_common_test_helpers:stop_apps([emqx_dashboard]),
|
||||||
Config.
|
Config.
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,10 @@ NOTE: The position should be \"front | rear | before:{name} | after:{name}"""
|
||||||
zh: """移动 Exhook 服务器顺序。
|
zh: """移动 Exhook 服务器顺序。
|
||||||
注意: 移动的参数只能是:front | rear | before:{name} | after:{name}"""
|
注意: 移动的参数只能是:front | rear | before:{name} | after:{name}"""
|
||||||
}
|
}
|
||||||
|
label {
|
||||||
|
en: "Change order of execution for registered Exhook server"
|
||||||
|
zh: "改变已注册的Exhook服务器的执行顺序"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
move_position {
|
move_position {
|
||||||
|
|
|
@ -180,7 +180,7 @@ schema("/gateways") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(list_gateway),
|
desc => ?DESC(list_gateway),
|
||||||
summary => <<"List All Gateways">>,
|
summary => <<"List all gateways">>,
|
||||||
parameters => params_gateway_status_in_qs(),
|
parameters => params_gateway_status_in_qs(),
|
||||||
responses =>
|
responses =>
|
||||||
#{
|
#{
|
||||||
|
@ -201,7 +201,7 @@ schema("/gateways/:name") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(get_gateway),
|
desc => ?DESC(get_gateway),
|
||||||
summary => <<"Get the Gateway">>,
|
summary => <<"Get gateway">>,
|
||||||
parameters => params_gateway_name_in_path(),
|
parameters => params_gateway_name_in_path(),
|
||||||
responses =>
|
responses =>
|
||||||
#{
|
#{
|
||||||
|
@ -608,7 +608,7 @@ examples_gateway_confs() ->
|
||||||
#{
|
#{
|
||||||
stomp_gateway =>
|
stomp_gateway =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple STOMP gateway configs">>,
|
summary => <<"A simple STOMP gateway config">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
enable => true,
|
enable => true,
|
||||||
|
@ -636,7 +636,7 @@ examples_gateway_confs() ->
|
||||||
},
|
},
|
||||||
mqttsn_gateway =>
|
mqttsn_gateway =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple MQTT-SN gateway configs">>,
|
summary => <<"A simple MQTT-SN gateway config">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
enable => true,
|
enable => true,
|
||||||
|
@ -672,7 +672,7 @@ examples_gateway_confs() ->
|
||||||
},
|
},
|
||||||
coap_gateway =>
|
coap_gateway =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple CoAP gateway configs">>,
|
summary => <<"A simple CoAP gateway config">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
enable => true,
|
enable => true,
|
||||||
|
@ -699,7 +699,7 @@ examples_gateway_confs() ->
|
||||||
},
|
},
|
||||||
lwm2m_gateway =>
|
lwm2m_gateway =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple LwM2M gateway configs">>,
|
summary => <<"A simple LwM2M gateway config">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
enable => true,
|
enable => true,
|
||||||
|
@ -735,7 +735,7 @@ examples_gateway_confs() ->
|
||||||
},
|
},
|
||||||
exproto_gateway =>
|
exproto_gateway =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple ExProto gateway configs">>,
|
summary => <<"A simple ExProto gateway config">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
enable => true,
|
enable => true,
|
||||||
|
@ -765,7 +765,7 @@ examples_update_gateway_confs() ->
|
||||||
#{
|
#{
|
||||||
stomp_gateway =>
|
stomp_gateway =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple STOMP gateway configs">>,
|
summary => <<"A simple STOMP gateway config">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
enable => true,
|
enable => true,
|
||||||
|
@ -782,7 +782,7 @@ examples_update_gateway_confs() ->
|
||||||
},
|
},
|
||||||
mqttsn_gateway =>
|
mqttsn_gateway =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple MQTT-SN gateway configs">>,
|
summary => <<"A simple MQTT-SN gateway config">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
enable => true,
|
enable => true,
|
||||||
|
@ -803,7 +803,7 @@ examples_update_gateway_confs() ->
|
||||||
},
|
},
|
||||||
coap_gateway =>
|
coap_gateway =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple CoAP gateway configs">>,
|
summary => <<"A simple CoAP gateway config">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
enable => true,
|
enable => true,
|
||||||
|
@ -819,7 +819,7 @@ examples_update_gateway_confs() ->
|
||||||
},
|
},
|
||||||
lwm2m_gateway =>
|
lwm2m_gateway =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple LwM2M gateway configs">>,
|
summary => <<"A simple LwM2M gateway config">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
enable => true,
|
enable => true,
|
||||||
|
@ -844,7 +844,7 @@ examples_update_gateway_confs() ->
|
||||||
},
|
},
|
||||||
exproto_gateway =>
|
exproto_gateway =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple ExProto gateway configs">>,
|
summary => <<"A simple ExProto gateway config">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
enable => true,
|
enable => true,
|
||||||
|
|
|
@ -185,13 +185,13 @@ schema("/gateways/:name/authentication") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(get_authn),
|
desc => ?DESC(get_authn),
|
||||||
summary => <<"Get Authenticator Configuration">>,
|
summary => <<"Get authenticator configuration">>,
|
||||||
parameters => params_gateway_name_in_path(),
|
parameters => params_gateway_name_in_path(),
|
||||||
responses =>
|
responses =>
|
||||||
?STANDARD_RESP(
|
?STANDARD_RESP(
|
||||||
#{
|
#{
|
||||||
200 => schema_authn(),
|
200 => schema_authn(),
|
||||||
204 => <<"Authenticator doesn't initiated">>
|
204 => <<"Authenticator not initialized">>
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -199,7 +199,7 @@ schema("/gateways/:name/authentication") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(update_authn),
|
desc => ?DESC(update_authn),
|
||||||
summary => <<"Update Authenticator Configuration">>,
|
summary => <<"Update authenticator configuration">>,
|
||||||
parameters => params_gateway_name_in_path(),
|
parameters => params_gateway_name_in_path(),
|
||||||
'requestBody' => schema_authn(),
|
'requestBody' => schema_authn(),
|
||||||
responses =>
|
responses =>
|
||||||
|
@ -209,7 +209,7 @@ schema("/gateways/:name/authentication") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(add_authn),
|
desc => ?DESC(add_authn),
|
||||||
summary => <<"Create an Authenticator for a Gateway">>,
|
summary => <<"Create authenticator for gateway">>,
|
||||||
parameters => params_gateway_name_in_path(),
|
parameters => params_gateway_name_in_path(),
|
||||||
'requestBody' => schema_authn(),
|
'requestBody' => schema_authn(),
|
||||||
responses =>
|
responses =>
|
||||||
|
@ -219,7 +219,7 @@ schema("/gateways/:name/authentication") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(delete_authn),
|
desc => ?DESC(delete_authn),
|
||||||
summary => <<"Delete the Gateway Authenticator">>,
|
summary => <<"Delete gateway authenticator">>,
|
||||||
parameters => params_gateway_name_in_path(),
|
parameters => params_gateway_name_in_path(),
|
||||||
responses =>
|
responses =>
|
||||||
?STANDARD_RESP(#{204 => <<"Deleted">>})
|
?STANDARD_RESP(#{204 => <<"Deleted">>})
|
||||||
|
@ -232,7 +232,7 @@ schema("/gateways/:name/authentication/users") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(list_users),
|
desc => ?DESC(list_users),
|
||||||
summary => <<"List users for a Gateway Authenticator">>,
|
summary => <<"List users for gateway authenticator">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_paging_in_qs() ++
|
params_paging_in_qs() ++
|
||||||
params_fuzzy_in_qs(),
|
params_fuzzy_in_qs(),
|
||||||
|
@ -250,7 +250,7 @@ schema("/gateways/:name/authentication/users") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(add_user),
|
desc => ?DESC(add_user),
|
||||||
summary => <<"Add User for a Gateway Authenticator">>,
|
summary => <<"Add user for gateway authenticator">>,
|
||||||
parameters => params_gateway_name_in_path(),
|
parameters => params_gateway_name_in_path(),
|
||||||
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
ref(emqx_authn_api, request_user_create),
|
ref(emqx_authn_api, request_user_create),
|
||||||
|
@ -274,7 +274,7 @@ schema("/gateways/:name/authentication/users/:uid") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(get_user),
|
desc => ?DESC(get_user),
|
||||||
summary => <<"Get User Info for a Gateway Authenticator">>,
|
summary => <<"Get user info for gateway authenticator">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_userid_in_path(),
|
params_userid_in_path(),
|
||||||
responses =>
|
responses =>
|
||||||
|
@ -291,7 +291,7 @@ schema("/gateways/:name/authentication/users/:uid") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(update_user),
|
desc => ?DESC(update_user),
|
||||||
summary => <<"Update User Info for a Gateway Authenticator">>,
|
summary => <<"Update user info for gateway authenticator">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_userid_in_path(),
|
params_userid_in_path(),
|
||||||
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
@ -312,7 +312,7 @@ schema("/gateways/:name/authentication/users/:uid") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(delete_user),
|
desc => ?DESC(delete_user),
|
||||||
summary => <<"Delete User for a Gateway Authenticator">>,
|
summary => <<"Delete user for gateway authenticator">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_userid_in_path(),
|
params_userid_in_path(),
|
||||||
responses =>
|
responses =>
|
||||||
|
|
|
@ -126,7 +126,7 @@ schema("/gateways/:name/authentication/import_users") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(emqx_gateway_api_authn, import_users),
|
desc => ?DESC(emqx_gateway_api_authn, import_users),
|
||||||
summary => <<"Import Users">>,
|
summary => <<"Import users">>,
|
||||||
parameters => params_gateway_name_in_path(),
|
parameters => params_gateway_name_in_path(),
|
||||||
'requestBody' => emqx_dashboard_swagger:file_schema(filename),
|
'requestBody' => emqx_dashboard_swagger:file_schema(filename),
|
||||||
responses =>
|
responses =>
|
||||||
|
@ -140,7 +140,7 @@ schema("/gateways/:name/listeners/:id/authentication/import_users") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(emqx_gateway_api_listeners, import_users),
|
desc => ?DESC(emqx_gateway_api_listeners, import_users),
|
||||||
summary => <<"Import Users">>,
|
summary => <<"Import users">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path(),
|
params_listener_id_in_path(),
|
||||||
'requestBody' => emqx_dashboard_swagger:file_schema(filename),
|
'requestBody' => emqx_dashboard_swagger:file_schema(filename),
|
||||||
|
|
|
@ -460,7 +460,7 @@ schema("/gateways/:name/clients") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(list_clients),
|
desc => ?DESC(list_clients),
|
||||||
summary => <<"List Gateway's Clients">>,
|
summary => <<"List gateway's clients">>,
|
||||||
parameters => params_client_query(),
|
parameters => params_client_query(),
|
||||||
responses =>
|
responses =>
|
||||||
?STANDARD_RESP(#{
|
?STANDARD_RESP(#{
|
||||||
|
@ -478,7 +478,7 @@ schema("/gateways/:name/clients/:clientid") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(get_client),
|
desc => ?DESC(get_client),
|
||||||
summary => <<"Get Client Info">>,
|
summary => <<"Get client info">>,
|
||||||
parameters => params_client_insta(),
|
parameters => params_client_insta(),
|
||||||
responses =>
|
responses =>
|
||||||
?STANDARD_RESP(#{200 => schema_client()})
|
?STANDARD_RESP(#{200 => schema_client()})
|
||||||
|
@ -487,7 +487,7 @@ schema("/gateways/:name/clients/:clientid") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(kick_client),
|
desc => ?DESC(kick_client),
|
||||||
summary => <<"Kick out Client">>,
|
summary => <<"Kick out client">>,
|
||||||
parameters => params_client_insta(),
|
parameters => params_client_insta(),
|
||||||
responses =>
|
responses =>
|
||||||
?STANDARD_RESP(#{204 => <<"Kicked">>})
|
?STANDARD_RESP(#{204 => <<"Kicked">>})
|
||||||
|
@ -500,7 +500,7 @@ schema("/gateways/:name/clients/:clientid/subscriptions") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(list_subscriptions),
|
desc => ?DESC(list_subscriptions),
|
||||||
summary => <<"List Client's Subscription">>,
|
summary => <<"List client's subscription">>,
|
||||||
parameters => params_client_insta(),
|
parameters => params_client_insta(),
|
||||||
responses =>
|
responses =>
|
||||||
?STANDARD_RESP(
|
?STANDARD_RESP(
|
||||||
|
@ -516,7 +516,7 @@ schema("/gateways/:name/clients/:clientid/subscriptions") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(add_subscription),
|
desc => ?DESC(add_subscription),
|
||||||
summary => <<"Add Subscription for Client">>,
|
summary => <<"Add subscription for client">>,
|
||||||
parameters => params_client_insta(),
|
parameters => params_client_insta(),
|
||||||
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
ref(subscription),
|
ref(subscription),
|
||||||
|
@ -540,7 +540,7 @@ schema("/gateways/:name/clients/:clientid/subscriptions/:topic") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(delete_subscription),
|
desc => ?DESC(delete_subscription),
|
||||||
summary => <<"Delete Client's Subscription">>,
|
summary => <<"Delete client's subscription">>,
|
||||||
parameters => params_topic_name_in_path() ++ params_client_insta(),
|
parameters => params_topic_name_in_path() ++ params_client_insta(),
|
||||||
responses =>
|
responses =>
|
||||||
?STANDARD_RESP(#{204 => <<"Unsubscribed">>})
|
?STANDARD_RESP(#{204 => <<"Unsubscribed">>})
|
||||||
|
@ -1020,12 +1020,12 @@ examples_client_list() ->
|
||||||
#{
|
#{
|
||||||
general_client_list =>
|
general_client_list =>
|
||||||
#{
|
#{
|
||||||
summary => <<"General Client List">>,
|
summary => <<"General client list">>,
|
||||||
value => [example_general_client()]
|
value => [example_general_client()]
|
||||||
},
|
},
|
||||||
lwm2m_client_list =>
|
lwm2m_client_list =>
|
||||||
#{
|
#{
|
||||||
summary => <<"LwM2M Client List">>,
|
summary => <<"LwM2M client list">>,
|
||||||
value => [example_lwm2m_client()]
|
value => [example_lwm2m_client()]
|
||||||
}
|
}
|
||||||
}.
|
}.
|
||||||
|
@ -1034,12 +1034,12 @@ examples_client() ->
|
||||||
#{
|
#{
|
||||||
general_client =>
|
general_client =>
|
||||||
#{
|
#{
|
||||||
summary => <<"General Client Info">>,
|
summary => <<"General client info">>,
|
||||||
value => example_general_client()
|
value => example_general_client()
|
||||||
},
|
},
|
||||||
lwm2m_client =>
|
lwm2m_client =>
|
||||||
#{
|
#{
|
||||||
summary => <<"LwM2M Client Info">>,
|
summary => <<"LwM2M client info">>,
|
||||||
value => example_lwm2m_client()
|
value => example_lwm2m_client()
|
||||||
}
|
}
|
||||||
}.
|
}.
|
||||||
|
@ -1048,12 +1048,12 @@ examples_subscription_list() ->
|
||||||
#{
|
#{
|
||||||
general_subscription_list =>
|
general_subscription_list =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A General Subscription List">>,
|
summary => <<"A general subscription list">>,
|
||||||
value => [example_general_subscription()]
|
value => [example_general_subscription()]
|
||||||
},
|
},
|
||||||
stomp_subscription_list =>
|
stomp_subscription_list =>
|
||||||
#{
|
#{
|
||||||
summary => <<"The Stomp Subscription List">>,
|
summary => <<"The STOMP subscription list">>,
|
||||||
value => [example_stomp_subscription]
|
value => [example_stomp_subscription]
|
||||||
}
|
}
|
||||||
}.
|
}.
|
||||||
|
@ -1062,12 +1062,12 @@ examples_subscription() ->
|
||||||
#{
|
#{
|
||||||
general_subscription =>
|
general_subscription =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A General Subscription">>,
|
summary => <<"A general subscription">>,
|
||||||
value => example_general_subscription()
|
value => example_general_subscription()
|
||||||
},
|
},
|
||||||
stomp_subscription =>
|
stomp_subscription =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A Stomp Subscription">>,
|
summary => <<"A STOMP subscription">>,
|
||||||
value => example_stomp_subscription()
|
value => example_stomp_subscription()
|
||||||
}
|
}
|
||||||
}.
|
}.
|
||||||
|
|
|
@ -362,7 +362,7 @@ schema("/gateways/:name/listeners") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(list_listeners),
|
desc => ?DESC(list_listeners),
|
||||||
summary => <<"List All Listeners">>,
|
summary => <<"List all listeners">>,
|
||||||
parameters => params_gateway_name_in_path(),
|
parameters => params_gateway_name_in_path(),
|
||||||
responses =>
|
responses =>
|
||||||
?STANDARD_RESP(
|
?STANDARD_RESP(
|
||||||
|
@ -378,7 +378,7 @@ schema("/gateways/:name/listeners") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(add_listener),
|
desc => ?DESC(add_listener),
|
||||||
summary => <<"Add a Listener">>,
|
summary => <<"Add listener">>,
|
||||||
parameters => params_gateway_name_in_path(),
|
parameters => params_gateway_name_in_path(),
|
||||||
%% XXX: How to distinguish the different listener supported by
|
%% XXX: How to distinguish the different listener supported by
|
||||||
%% different types of gateways?
|
%% different types of gateways?
|
||||||
|
@ -404,7 +404,7 @@ schema("/gateways/:name/listeners/:id") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(get_listener),
|
desc => ?DESC(get_listener),
|
||||||
summary => <<"Get the Listener Configs">>,
|
summary => <<"Get listener config">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path(),
|
params_listener_id_in_path(),
|
||||||
responses =>
|
responses =>
|
||||||
|
@ -421,7 +421,7 @@ schema("/gateways/:name/listeners/:id") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(delete_listener),
|
desc => ?DESC(delete_listener),
|
||||||
summary => <<"Delete the Listener">>,
|
summary => <<"Delete listener">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path(),
|
params_listener_id_in_path(),
|
||||||
responses =>
|
responses =>
|
||||||
|
@ -431,7 +431,7 @@ schema("/gateways/:name/listeners/:id") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(update_listener),
|
desc => ?DESC(update_listener),
|
||||||
summary => <<"Update the Listener Configs">>,
|
summary => <<"Update listener config">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path(),
|
params_listener_id_in_path(),
|
||||||
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
@ -456,7 +456,7 @@ schema("/gateways/:name/listeners/:id/authentication") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(get_listener_authn),
|
desc => ?DESC(get_listener_authn),
|
||||||
summary => <<"Get the Listener's Authenticator">>,
|
summary => <<"Get the listener's authenticator">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path(),
|
params_listener_id_in_path(),
|
||||||
responses =>
|
responses =>
|
||||||
|
@ -471,7 +471,7 @@ schema("/gateways/:name/listeners/:id/authentication") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(add_listener_authn),
|
desc => ?DESC(add_listener_authn),
|
||||||
summary => <<"Create an Authenticator for a Listener">>,
|
summary => <<"Create authenticator for listener">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path(),
|
params_listener_id_in_path(),
|
||||||
'requestBody' => schema_authn(),
|
'requestBody' => schema_authn(),
|
||||||
|
@ -482,7 +482,7 @@ schema("/gateways/:name/listeners/:id/authentication") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(update_listener_authn),
|
desc => ?DESC(update_listener_authn),
|
||||||
summary => <<"Update the Listener Authenticator configs">>,
|
summary => <<"Update config of authenticator for listener">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path(),
|
params_listener_id_in_path(),
|
||||||
'requestBody' => schema_authn(),
|
'requestBody' => schema_authn(),
|
||||||
|
@ -493,7 +493,7 @@ schema("/gateways/:name/listeners/:id/authentication") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(delete_listener_authn),
|
desc => ?DESC(delete_listener_authn),
|
||||||
summary => <<"Delete the Listener's Authenticator">>,
|
summary => <<"Delete the listener's authenticator">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path(),
|
params_listener_id_in_path(),
|
||||||
responses =>
|
responses =>
|
||||||
|
@ -507,7 +507,7 @@ schema("/gateways/:name/listeners/:id/authentication/users") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(list_users),
|
desc => ?DESC(list_users),
|
||||||
summary => <<"List Authenticator's Users">>,
|
summary => <<"List authenticator's users">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path() ++
|
params_listener_id_in_path() ++
|
||||||
params_paging_in_qs(),
|
params_paging_in_qs(),
|
||||||
|
@ -525,7 +525,7 @@ schema("/gateways/:name/listeners/:id/authentication/users") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(add_user),
|
desc => ?DESC(add_user),
|
||||||
summary => <<"Add User for an Authenticator">>,
|
summary => <<"Add user for an authenticator">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path(),
|
params_listener_id_in_path(),
|
||||||
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
'requestBody' => emqx_dashboard_swagger:schema_with_examples(
|
||||||
|
@ -550,7 +550,7 @@ schema("/gateways/:name/listeners/:id/authentication/users/:uid") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(get_user),
|
desc => ?DESC(get_user),
|
||||||
summary => <<"Get User Info">>,
|
summary => <<"Get user info">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path() ++
|
params_listener_id_in_path() ++
|
||||||
params_userid_in_path(),
|
params_userid_in_path(),
|
||||||
|
@ -568,7 +568,7 @@ schema("/gateways/:name/listeners/:id/authentication/users/:uid") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(update_user),
|
desc => ?DESC(update_user),
|
||||||
summary => <<"Update User Info">>,
|
summary => <<"Update user info">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path() ++
|
params_listener_id_in_path() ++
|
||||||
params_userid_in_path(),
|
params_userid_in_path(),
|
||||||
|
@ -590,7 +590,7 @@ schema("/gateways/:name/listeners/:id/authentication/users/:uid") ->
|
||||||
#{
|
#{
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
desc => ?DESC(delete_user),
|
desc => ?DESC(delete_user),
|
||||||
summary => <<"Delete User">>,
|
summary => <<"Delete user">>,
|
||||||
parameters => params_gateway_name_in_path() ++
|
parameters => params_gateway_name_in_path() ++
|
||||||
params_listener_id_in_path() ++
|
params_listener_id_in_path() ++
|
||||||
params_userid_in_path(),
|
params_userid_in_path(),
|
||||||
|
@ -712,7 +712,7 @@ examples_listener() ->
|
||||||
#{
|
#{
|
||||||
tcp_listener =>
|
tcp_listener =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple tcp listener example">>,
|
summary => <<"A simple TCP listener example">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
name => <<"tcp-def">>,
|
name => <<"tcp-def">>,
|
||||||
|
@ -738,7 +738,7 @@ examples_listener() ->
|
||||||
},
|
},
|
||||||
ssl_listener =>
|
ssl_listener =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple ssl listener example">>,
|
summary => <<"A simple SSL listener example">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
name => <<"ssl-def">>,
|
name => <<"ssl-def">>,
|
||||||
|
@ -771,7 +771,7 @@ examples_listener() ->
|
||||||
},
|
},
|
||||||
udp_listener =>
|
udp_listener =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple udp listener example">>,
|
summary => <<"A simple UDP listener example">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
name => <<"udp-def">>,
|
name => <<"udp-def">>,
|
||||||
|
@ -789,7 +789,7 @@ examples_listener() ->
|
||||||
},
|
},
|
||||||
dtls_listener =>
|
dtls_listener =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A simple dtls listener example">>,
|
summary => <<"A simple DTLS listener example">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
name => <<"dtls-def">>,
|
name => <<"dtls-def">>,
|
||||||
|
@ -817,7 +817,7 @@ examples_listener() ->
|
||||||
},
|
},
|
||||||
dtls_listener_with_psk_ciphers =>
|
dtls_listener_with_psk_ciphers =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A dtls listener with PSK example">>,
|
summary => <<"A DTLS listener with PSK example">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
name => <<"dtls-psk">>,
|
name => <<"dtls-psk">>,
|
||||||
|
@ -845,7 +845,7 @@ examples_listener() ->
|
||||||
},
|
},
|
||||||
lisetner_with_authn =>
|
lisetner_with_authn =>
|
||||||
#{
|
#{
|
||||||
summary => <<"A tcp listener with authentication example">>,
|
summary => <<"A TCP listener with authentication example">>,
|
||||||
value =>
|
value =>
|
||||||
#{
|
#{
|
||||||
name => <<"tcp-with-authn">>,
|
name => <<"tcp-with-authn">>,
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
emqx_mgmt_api_publish {
|
emqx_mgmt_api_publish {
|
||||||
publish_api {
|
publish_api {
|
||||||
desc {
|
desc {
|
||||||
en: """Publish one message.<br/>
|
en: """Possible HTTP status response codes are:<br/>
|
||||||
Possible HTTP status response codes are:<br/>
|
|
||||||
<code>200</code>: The message is delivered to at least one subscriber;<br/>
|
<code>200</code>: The message is delivered to at least one subscriber;<br/>
|
||||||
<code>202</code>: No matched subscribers;<br/>
|
<code>202</code>: No matched subscribers;<br/>
|
||||||
<code>400</code>: Message is invalid. for example bad topic name, or QoS is out of range;<br/>
|
<code>400</code>: Message is invalid. for example bad topic name, or QoS is out of range;<br/>
|
||||||
|
@ -16,11 +15,14 @@ Possible HTTP status response codes are:<br/>
|
||||||
400: 消息编码错误,如非法主题,或 QoS 超出范围等。<br/>
|
400: 消息编码错误,如非法主题,或 QoS 超出范围等。<br/>
|
||||||
503: 服务重启等过程中导致转发失败。"""
|
503: 服务重启等过程中导致转发失败。"""
|
||||||
}
|
}
|
||||||
|
label {
|
||||||
|
en: "Publish a message"
|
||||||
|
zh: "发布一条信息"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
publish_bulk_api {
|
publish_bulk_api {
|
||||||
desc {
|
desc {
|
||||||
en: """Publish a batch of messages.<br/>
|
en: """Possible HTTP response status code are:<br/>
|
||||||
Possible HTTP response status code are:<br/>
|
|
||||||
200: All messages are delivered to at least one subscriber;<br/>
|
200: All messages are delivered to at least one subscriber;<br/>
|
||||||
202: At least one message was not delivered to any subscriber;<br/>
|
202: At least one message was not delivered to any subscriber;<br/>
|
||||||
400: At least one message is invalid. For example bad topic name, or QoS is out of range;<br/>
|
400: At least one message is invalid. For example bad topic name, or QoS is out of range;<br/>
|
||||||
|
@ -41,6 +43,10 @@ result of each individual message in the batch."""
|
||||||
<code>/publish</code> 是一样的。
|
<code>/publish</code> 是一样的。
|
||||||
如果所有的消息都是合法的,那么 HTTP 返回的内容是一个 JSON 数组,每个元素代表了该消息转发的状态。"""
|
如果所有的消息都是合法的,那么 HTTP 返回的内容是一个 JSON 数组,每个元素代表了该消息转发的状态。"""
|
||||||
}
|
}
|
||||||
|
label {
|
||||||
|
en: "Publish a batch of messages"
|
||||||
|
zh: "发布一批信息"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
topic_name {
|
topic_name {
|
||||||
|
|
|
@ -22,6 +22,10 @@ emqx_mgmt_api_status {
|
||||||
"GET `/status`端点(没有`/api/...`前缀)也是这个端点的一个别名,工作方式相同。"
|
"GET `/status`端点(没有`/api/...`前缀)也是这个端点的一个别名,工作方式相同。"
|
||||||
" 这个别名从v5.0.0开始就有了。"
|
" 这个别名从v5.0.0开始就有了。"
|
||||||
}
|
}
|
||||||
|
label {
|
||||||
|
en: "Service health check"
|
||||||
|
zh: "服务健康检查"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get_status_response200 {
|
get_status_response200 {
|
||||||
|
|
|
@ -119,6 +119,7 @@ schema("/configs_reset/:rootname") ->
|
||||||
"- For a config entry that has default value, this resets it to the default value;\n"
|
"- For a config entry that has default value, this resets it to the default value;\n"
|
||||||
"- For a config entry that has no default value, an error 400 will be returned"
|
"- For a config entry that has no default value, an error 400 will be returned"
|
||||||
>>,
|
>>,
|
||||||
|
summary => <<"Reset config entry">>,
|
||||||
%% We only return "200" rather than the new configs that has been changed, as
|
%% We only return "200" rather than the new configs that has been changed, as
|
||||||
%% the schema of the changed configs is depends on the request parameter
|
%% the schema of the changed configs is depends on the request parameter
|
||||||
%% `conf_path`, it cannot be defined here.
|
%% `conf_path`, it cannot be defined here.
|
||||||
|
|
|
@ -48,6 +48,9 @@
|
||||||
|
|
||||||
-define(NAME_RE, "^[A-Za-z]+[A-Za-z0-9-_.]*$").
|
-define(NAME_RE, "^[A-Za-z]+[A-Za-z0-9-_.]*$").
|
||||||
-define(TAGS, [<<"Plugins">>]).
|
-define(TAGS, [<<"Plugins">>]).
|
||||||
|
%% Plugin NameVsn must follow the pattern <app_name>-<vsn>,
|
||||||
|
%% app_name must be a snake_case (no '-' allowed).
|
||||||
|
-define(VSN_WILDCARD, "-*.tar.gz").
|
||||||
|
|
||||||
namespace() -> "plugins".
|
namespace() -> "plugins".
|
||||||
|
|
||||||
|
@ -68,10 +71,10 @@ schema("/plugins") ->
|
||||||
#{
|
#{
|
||||||
'operationId' => list_plugins,
|
'operationId' => list_plugins,
|
||||||
get => #{
|
get => #{
|
||||||
|
summary => <<"List all installed plugins">>,
|
||||||
description =>
|
description =>
|
||||||
"List all install plugins.<br/>"
|
|
||||||
"Plugins are launched in top-down order.<br/>"
|
"Plugins are launched in top-down order.<br/>"
|
||||||
"Using `POST /plugins/{name}/move` to change the boot order.",
|
"Use `POST /plugins/{name}/move` to change the boot order.",
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => hoconsc:array(hoconsc:ref(plugin))
|
200 => hoconsc:array(hoconsc:ref(plugin))
|
||||||
|
@ -82,8 +85,9 @@ schema("/plugins/install") ->
|
||||||
#{
|
#{
|
||||||
'operationId' => upload_install,
|
'operationId' => upload_install,
|
||||||
post => #{
|
post => #{
|
||||||
|
summary => <<"Install a new plugin">>,
|
||||||
description =>
|
description =>
|
||||||
"Install a plugin(plugin-vsn.tar.gz)."
|
"Upload a plugin tarball (plugin-vsn.tar.gz)."
|
||||||
"Follow [emqx-plugin-template](https://github.com/emqx/emqx-plugin-template) "
|
"Follow [emqx-plugin-template](https://github.com/emqx/emqx-plugin-template) "
|
||||||
"to develop plugin.",
|
"to develop plugin.",
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
|
@ -112,7 +116,8 @@ schema("/plugins/:name") ->
|
||||||
#{
|
#{
|
||||||
'operationId' => plugin,
|
'operationId' => plugin,
|
||||||
get => #{
|
get => #{
|
||||||
description => "Describe a plugin according `release.json` and `README.md`.",
|
summary => <<"Get a plugin description">>,
|
||||||
|
description => "Describs plugin according to its `release.json` and `README.md`.",
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
parameters => [hoconsc:ref(name)],
|
parameters => [hoconsc:ref(name)],
|
||||||
responses => #{
|
responses => #{
|
||||||
|
@ -121,7 +126,8 @@ schema("/plugins/:name") ->
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
delete => #{
|
delete => #{
|
||||||
description => "Uninstall a plugin package.",
|
summary => <<"Delete a plugin">>,
|
||||||
|
description => "Uninstalls a previously uploaded plugin package.",
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
parameters => [hoconsc:ref(name)],
|
parameters => [hoconsc:ref(name)],
|
||||||
responses => #{
|
responses => #{
|
||||||
|
@ -134,6 +140,7 @@ schema("/plugins/:name/:action") ->
|
||||||
#{
|
#{
|
||||||
'operationId' => update_plugin,
|
'operationId' => update_plugin,
|
||||||
put => #{
|
put => #{
|
||||||
|
summary => <<"Trigger action on an installed plugin">>,
|
||||||
description =>
|
description =>
|
||||||
"start/stop a installed plugin.<br/>"
|
"start/stop a installed plugin.<br/>"
|
||||||
"- **start**: start the plugin.<br/>"
|
"- **start**: start the plugin.<br/>"
|
||||||
|
@ -153,6 +160,7 @@ schema("/plugins/:name/move") ->
|
||||||
#{
|
#{
|
||||||
'operationId' => update_boot_order,
|
'operationId' => update_boot_order,
|
||||||
post => #{
|
post => #{
|
||||||
|
summary => <<"Move plugin within plugin hiearchy">>,
|
||||||
description => "Setting the boot order of plugins.",
|
description => "Setting the boot order of plugins.",
|
||||||
tags => ?TAGS,
|
tags => ?TAGS,
|
||||||
parameters => [hoconsc:ref(name)],
|
parameters => [hoconsc:ref(name)],
|
||||||
|
@ -329,7 +337,7 @@ upload_install(post, #{body := #{<<"plugin">> := Plugin}}) when is_map(Plugin) -
|
||||||
case emqx_plugins:parse_name_vsn(FileName) of
|
case emqx_plugins:parse_name_vsn(FileName) of
|
||||||
{ok, AppName, _Vsn} ->
|
{ok, AppName, _Vsn} ->
|
||||||
AppDir = filename:join(emqx_plugins:install_dir(), AppName),
|
AppDir = filename:join(emqx_plugins:install_dir(), AppName),
|
||||||
case filelib:wildcard(AppDir ++ "*.tar.gz") of
|
case filelib:wildcard(AppDir ++ ?VSN_WILDCARD) of
|
||||||
[] ->
|
[] ->
|
||||||
do_install_package(FileName, Bin);
|
do_install_package(FileName, Bin);
|
||||||
OtherVsn ->
|
OtherVsn ->
|
||||||
|
|
|
@ -50,6 +50,7 @@ schema("/publish") ->
|
||||||
#{
|
#{
|
||||||
'operationId' => publish,
|
'operationId' => publish,
|
||||||
post => #{
|
post => #{
|
||||||
|
summary => <<"Publish a message">>,
|
||||||
description => ?DESC(publish_api),
|
description => ?DESC(publish_api),
|
||||||
tags => [<<"Publish">>],
|
tags => [<<"Publish">>],
|
||||||
'requestBody' => hoconsc:mk(hoconsc:ref(?MODULE, publish_message)),
|
'requestBody' => hoconsc:mk(hoconsc:ref(?MODULE, publish_message)),
|
||||||
|
@ -65,6 +66,7 @@ schema("/publish/bulk") ->
|
||||||
#{
|
#{
|
||||||
'operationId' => publish_batch,
|
'operationId' => publish_batch,
|
||||||
post => #{
|
post => #{
|
||||||
|
summary => <<"Publish a batch of messages">>,
|
||||||
description => ?DESC(publish_bulk_api),
|
description => ?DESC(publish_bulk_api),
|
||||||
tags => [<<"Publish">>],
|
tags => [<<"Publish">>],
|
||||||
'requestBody' => hoconsc:mk(hoconsc:array(hoconsc:ref(?MODULE, publish_message)), #{}),
|
'requestBody' => hoconsc:mk(hoconsc:array(hoconsc:ref(?MODULE, publish_message)), #{}),
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
-define(EMQX_PLUGIN_TEMPLATE_NAME, "emqx_plugin_template").
|
||||||
-define(EMQX_PLUGIN_TEMPLATE_VSN, "5.0.0").
|
-define(EMQX_PLUGIN_TEMPLATE_VSN, "5.0.0").
|
||||||
-define(PACKAGE_SUFFIX, ".tar.gz").
|
-define(PACKAGE_SUFFIX, ".tar.gz").
|
||||||
|
|
||||||
|
@ -89,6 +90,27 @@ t_plugins(Config) ->
|
||||||
{ok, []} = uninstall_plugin(NameVsn),
|
{ok, []} = uninstall_plugin(NameVsn),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
t_install_plugin_matching_exisiting_name(Config) ->
|
||||||
|
DemoShDir = proplists:get_value(demo_sh_dir, Config),
|
||||||
|
PackagePath = get_demo_plugin_package(DemoShDir),
|
||||||
|
NameVsn = filename:basename(PackagePath, ?PACKAGE_SUFFIX),
|
||||||
|
ok = emqx_plugins:ensure_uninstalled(NameVsn),
|
||||||
|
ok = emqx_plugins:delete_package(NameVsn),
|
||||||
|
NameVsn1 = ?EMQX_PLUGIN_TEMPLATE_NAME ++ "_a" ++ "-" ++ ?EMQX_PLUGIN_TEMPLATE_VSN,
|
||||||
|
PackagePath1 = create_renamed_package(PackagePath, NameVsn1),
|
||||||
|
NameVsn1 = filename:basename(PackagePath1, ?PACKAGE_SUFFIX),
|
||||||
|
ok = emqx_plugins:ensure_uninstalled(NameVsn1),
|
||||||
|
ok = emqx_plugins:delete_package(NameVsn1),
|
||||||
|
%% First, install plugin "emqx_plugin_template_a", then:
|
||||||
|
%% "emqx_plugin_template" which matches the beginning
|
||||||
|
%% of the previously installed plugin name
|
||||||
|
ok = install_plugin(PackagePath1),
|
||||||
|
ok = install_plugin(PackagePath),
|
||||||
|
{ok, _} = describe_plugins(NameVsn),
|
||||||
|
{ok, _} = describe_plugins(NameVsn1),
|
||||||
|
{ok, _} = uninstall_plugin(NameVsn),
|
||||||
|
{ok, _} = uninstall_plugin(NameVsn1).
|
||||||
|
|
||||||
t_bad_plugin(Config) ->
|
t_bad_plugin(Config) ->
|
||||||
DemoShDir = proplists:get_value(demo_sh_dir, Config),
|
DemoShDir = proplists:get_value(demo_sh_dir, Config),
|
||||||
PackagePathOrig = get_demo_plugin_package(DemoShDir),
|
PackagePathOrig = get_demo_plugin_package(DemoShDir),
|
||||||
|
@ -160,9 +182,31 @@ uninstall_plugin(Name) ->
|
||||||
|
|
||||||
get_demo_plugin_package(Dir) ->
|
get_demo_plugin_package(Dir) ->
|
||||||
#{package := Pkg} = emqx_plugins_SUITE:get_demo_plugin_package(),
|
#{package := Pkg} = emqx_plugins_SUITE:get_demo_plugin_package(),
|
||||||
FileName = "emqx_plugin_template-" ++ ?EMQX_PLUGIN_TEMPLATE_VSN ++ ?PACKAGE_SUFFIX,
|
FileName = ?EMQX_PLUGIN_TEMPLATE_NAME ++ "-" ++ ?EMQX_PLUGIN_TEMPLATE_VSN ++ ?PACKAGE_SUFFIX,
|
||||||
PluginPath = "./" ++ FileName,
|
PluginPath = "./" ++ FileName,
|
||||||
Pkg = filename:join([Dir, FileName]),
|
Pkg = filename:join([Dir, FileName]),
|
||||||
_ = os:cmd("cp " ++ Pkg ++ " " ++ PluginPath),
|
_ = os:cmd("cp " ++ Pkg ++ " " ++ PluginPath),
|
||||||
true = filelib:is_regular(PluginPath),
|
true = filelib:is_regular(PluginPath),
|
||||||
PluginPath.
|
PluginPath.
|
||||||
|
|
||||||
|
create_renamed_package(PackagePath, NewNameVsn) ->
|
||||||
|
{ok, Content} = erl_tar:extract(PackagePath, [compressed, memory]),
|
||||||
|
{ok, NewName, _Vsn} = emqx_plugins:parse_name_vsn(NewNameVsn),
|
||||||
|
NewNameB = atom_to_binary(NewName, utf8),
|
||||||
|
Content1 = lists:map(
|
||||||
|
fun({F, B}) ->
|
||||||
|
[_ | PathPart] = filename:split(F),
|
||||||
|
B1 = update_release_json(PathPart, B, NewNameB),
|
||||||
|
{filename:join([NewNameVsn | PathPart]), B1}
|
||||||
|
end,
|
||||||
|
Content
|
||||||
|
),
|
||||||
|
NewPackagePath = filename:join(filename:dirname(PackagePath), NewNameVsn ++ ?PACKAGE_SUFFIX),
|
||||||
|
ok = erl_tar:create(NewPackagePath, Content1, [compressed]),
|
||||||
|
NewPackagePath.
|
||||||
|
|
||||||
|
update_release_json(["release.json"], FileContent, NewName) ->
|
||||||
|
ContentMap = emqx_json:decode(FileContent, [return_maps]),
|
||||||
|
emqx_json:encode(ContentMap#{<<"name">> => NewName});
|
||||||
|
update_release_json(_FileName, FileContent, _NewName) ->
|
||||||
|
FileContent.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
emqx_topic_metrics_api {
|
emqx_topic_metrics_api {
|
||||||
get_topic_metrics_api {
|
get_topic_metrics_api {
|
||||||
desc {
|
desc {
|
||||||
en: """List Topic metrics"""
|
en: """List topic metrics"""
|
||||||
zh: """获取主题监控数据"""
|
zh: """获取主题监控数据"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,21 +15,21 @@ emqx_topic_metrics_api {
|
||||||
|
|
||||||
post_topic_metrics_api {
|
post_topic_metrics_api {
|
||||||
desc {
|
desc {
|
||||||
en: """Create Topic metrics"""
|
en: """Create topic metrics"""
|
||||||
zh: """创建主题监控数据"""
|
zh: """创建主题监控数据"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gat_topic_metrics_data_api {
|
gat_topic_metrics_data_api {
|
||||||
desc {
|
desc {
|
||||||
en: """Get Topic metrics"""
|
en: """Get topic metrics"""
|
||||||
zh: """获取主题监控数据"""
|
zh: """获取主题监控数据"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_topic_metrics_data_api {
|
delete_topic_metrics_data_api {
|
||||||
desc {
|
desc {
|
||||||
en: """Delete Topic metrics"""
|
en: """Delete topic metrics"""
|
||||||
zh: """删除主题监控数据"""
|
zh: """删除主题监控数据"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ emqx_topic_metrics_api {
|
||||||
|
|
||||||
topic_metrics_api_response400 {
|
topic_metrics_api_response400 {
|
||||||
desc {
|
desc {
|
||||||
en: """Bad Request. Already exists or bad topic name"""
|
en: """Bad request. Already exists or bad topic name"""
|
||||||
zh: """错误请求。已存在或错误的主题名称"""
|
zh: """错误请求。已存在或错误的主题名称"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,17 @@ For bridges only have ingress direction data flow, it can be set to 0 otherwise
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resume_interval {
|
||||||
|
desc {
|
||||||
|
en: """The interval at which the buffer worker attempts to resend failed requests in the inflight window."""
|
||||||
|
zh: """在发送失败后尝试重传飞行窗口中的请求的时间间隔。"""
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
en: """Resume Interval"""
|
||||||
|
zh: """重试时间间隔"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
start_after_created {
|
start_after_created {
|
||||||
desc {
|
desc {
|
||||||
en: """Whether start the resource right after created."""
|
en: """Whether start the resource right after created."""
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
callback_mode := callback_mode(),
|
callback_mode := callback_mode(),
|
||||||
query_mode := query_mode(),
|
query_mode := query_mode(),
|
||||||
config := resource_config(),
|
config := resource_config(),
|
||||||
|
error := term(),
|
||||||
state := resource_state(),
|
state := resource_state(),
|
||||||
status := resource_status(),
|
status := resource_status(),
|
||||||
metrics => emqx_metrics_worker:metrics()
|
metrics => emqx_metrics_worker:metrics()
|
||||||
|
|
|
@ -265,7 +265,7 @@ query(ResId, Request, Opts) ->
|
||||||
IsBufferSupported = is_buffer_supported(Module),
|
IsBufferSupported = is_buffer_supported(Module),
|
||||||
case {IsBufferSupported, QM} of
|
case {IsBufferSupported, QM} of
|
||||||
{true, _} ->
|
{true, _} ->
|
||||||
%% only Kafka so far
|
%% only Kafka producer so far
|
||||||
Opts1 = Opts#{is_buffer_supported => true},
|
Opts1 = Opts#{is_buffer_supported => true},
|
||||||
emqx_resource_buffer_worker:simple_async_query(ResId, Request, Opts1);
|
emqx_resource_buffer_worker:simple_async_query(ResId, Request, Opts1);
|
||||||
{false, sync} ->
|
{false, sync} ->
|
||||||
|
|
|
@ -88,6 +88,8 @@
|
||||||
-type queue_query() :: ?QUERY(reply_fun(), request(), HasBeenSent :: boolean(), expire_at()).
|
-type queue_query() :: ?QUERY(reply_fun(), request(), HasBeenSent :: boolean(), expire_at()).
|
||||||
-type request() :: term().
|
-type request() :: term().
|
||||||
-type request_from() :: undefined | gen_statem:from().
|
-type request_from() :: undefined | gen_statem:from().
|
||||||
|
-type request_timeout() :: infinity | timer:time().
|
||||||
|
-type health_check_interval() :: timer:time().
|
||||||
-type state() :: blocked | running.
|
-type state() :: blocked | running.
|
||||||
-type inflight_key() :: integer().
|
-type inflight_key() :: integer().
|
||||||
-type data() :: #{
|
-type data() :: #{
|
||||||
|
@ -140,7 +142,7 @@ simple_sync_query(Id, Request) ->
|
||||||
QueryOpts = simple_query_opts(),
|
QueryOpts = simple_query_opts(),
|
||||||
emqx_resource_metrics:matched_inc(Id),
|
emqx_resource_metrics:matched_inc(Id),
|
||||||
Ref = make_request_ref(),
|
Ref = make_request_ref(),
|
||||||
Result = call_query(sync, Id, Index, Ref, ?SIMPLE_QUERY(Request), QueryOpts),
|
Result = call_query(force_sync, Id, Index, Ref, ?SIMPLE_QUERY(Request), QueryOpts),
|
||||||
_ = handle_query_result(Id, Result, _HasBeenSent = false),
|
_ = handle_query_result(Id, Result, _HasBeenSent = false),
|
||||||
Result.
|
Result.
|
||||||
|
|
||||||
|
@ -152,7 +154,7 @@ simple_async_query(Id, Request, QueryOpts0) ->
|
||||||
QueryOpts = maps:merge(simple_query_opts(), QueryOpts0),
|
QueryOpts = maps:merge(simple_query_opts(), QueryOpts0),
|
||||||
emqx_resource_metrics:matched_inc(Id),
|
emqx_resource_metrics:matched_inc(Id),
|
||||||
Ref = make_request_ref(),
|
Ref = make_request_ref(),
|
||||||
Result = call_query(async, Id, Index, Ref, ?SIMPLE_QUERY(Request), QueryOpts),
|
Result = call_query(async_if_possible, Id, Index, Ref, ?SIMPLE_QUERY(Request), QueryOpts),
|
||||||
_ = handle_query_result(Id, Result, _HasBeenSent = false),
|
_ = handle_query_result(Id, Result, _HasBeenSent = false),
|
||||||
Result.
|
Result.
|
||||||
|
|
||||||
|
@ -199,6 +201,8 @@ init({Id, Index, Opts}) ->
|
||||||
RequestTimeout = maps:get(request_timeout, Opts, ?DEFAULT_REQUEST_TIMEOUT),
|
RequestTimeout = maps:get(request_timeout, Opts, ?DEFAULT_REQUEST_TIMEOUT),
|
||||||
BatchTime0 = maps:get(batch_time, Opts, ?DEFAULT_BATCH_TIME),
|
BatchTime0 = maps:get(batch_time, Opts, ?DEFAULT_BATCH_TIME),
|
||||||
BatchTime = adjust_batch_time(Id, RequestTimeout, BatchTime0),
|
BatchTime = adjust_batch_time(Id, RequestTimeout, BatchTime0),
|
||||||
|
DefaultResumeInterval = default_resume_interval(RequestTimeout, HealthCheckInterval),
|
||||||
|
ResumeInterval = maps:get(resume_interval, Opts, DefaultResumeInterval),
|
||||||
Data = #{
|
Data = #{
|
||||||
id => Id,
|
id => Id,
|
||||||
index => Index,
|
index => Index,
|
||||||
|
@ -207,7 +211,7 @@ init({Id, Index, Opts}) ->
|
||||||
batch_size => BatchSize,
|
batch_size => BatchSize,
|
||||||
batch_time => BatchTime,
|
batch_time => BatchTime,
|
||||||
queue => Queue,
|
queue => Queue,
|
||||||
resume_interval => maps:get(resume_interval, Opts, HealthCheckInterval),
|
resume_interval => ResumeInterval,
|
||||||
tref => undefined
|
tref => undefined
|
||||||
},
|
},
|
||||||
?tp(buffer_worker_init, #{id => Id, index => Index}),
|
?tp(buffer_worker_init, #{id => Id, index => Index}),
|
||||||
|
@ -377,7 +381,7 @@ retry_inflight_sync(Ref, QueryOrBatch, Data0) ->
|
||||||
} = Data0,
|
} = Data0,
|
||||||
?tp(buffer_worker_retry_inflight, #{query_or_batch => QueryOrBatch, ref => Ref}),
|
?tp(buffer_worker_retry_inflight, #{query_or_batch => QueryOrBatch, ref => Ref}),
|
||||||
QueryOpts = #{simple_query => false},
|
QueryOpts = #{simple_query => false},
|
||||||
Result = call_query(sync, Id, Index, Ref, QueryOrBatch, QueryOpts),
|
Result = call_query(force_sync, Id, Index, Ref, QueryOrBatch, QueryOpts),
|
||||||
ReplyResult =
|
ReplyResult =
|
||||||
case QueryOrBatch of
|
case QueryOrBatch of
|
||||||
?QUERY(ReplyTo, _, HasBeenSent, _ExpireAt) ->
|
?QUERY(ReplyTo, _, HasBeenSent, _ExpireAt) ->
|
||||||
|
@ -566,7 +570,7 @@ do_flush(
|
||||||
%% unwrap when not batching (i.e., batch size == 1)
|
%% unwrap when not batching (i.e., batch size == 1)
|
||||||
[?QUERY(ReplyTo, _, HasBeenSent, _ExpireAt) = Request] = Batch,
|
[?QUERY(ReplyTo, _, HasBeenSent, _ExpireAt) = Request] = Batch,
|
||||||
QueryOpts = #{inflight_tid => InflightTID, simple_query => false},
|
QueryOpts = #{inflight_tid => InflightTID, simple_query => false},
|
||||||
Result = call_query(configured, Id, Index, Ref, Request, QueryOpts),
|
Result = call_query(async_if_possible, Id, Index, Ref, Request, QueryOpts),
|
||||||
Reply = ?REPLY(ReplyTo, HasBeenSent, Result),
|
Reply = ?REPLY(ReplyTo, HasBeenSent, Result),
|
||||||
case reply_caller(Id, Reply, QueryOpts) of
|
case reply_caller(Id, Reply, QueryOpts) of
|
||||||
%% Failed; remove the request from the queue, as we cannot pop
|
%% Failed; remove the request from the queue, as we cannot pop
|
||||||
|
@ -651,7 +655,7 @@ do_flush(#{queue := Q1} = Data0, #{
|
||||||
inflight_tid := InflightTID
|
inflight_tid := InflightTID
|
||||||
} = Data0,
|
} = Data0,
|
||||||
QueryOpts = #{inflight_tid => InflightTID, simple_query => false},
|
QueryOpts = #{inflight_tid => InflightTID, simple_query => false},
|
||||||
Result = call_query(configured, Id, Index, Ref, Batch, QueryOpts),
|
Result = call_query(async_if_possible, Id, Index, Ref, Batch, QueryOpts),
|
||||||
case batch_reply_caller(Id, Result, Batch, QueryOpts) of
|
case batch_reply_caller(Id, Result, Batch, QueryOpts) of
|
||||||
%% Failed; remove the request from the queue, as we cannot pop
|
%% Failed; remove the request from the queue, as we cannot pop
|
||||||
%% from it again, but we'll retry it using the inflight table.
|
%% from it again, but we'll retry it using the inflight table.
|
||||||
|
@ -883,17 +887,13 @@ handle_async_worker_down(Data0, Pid) ->
|
||||||
mark_inflight_items_as_retriable(Data, WorkerMRef),
|
mark_inflight_items_as_retriable(Data, WorkerMRef),
|
||||||
{keep_state, Data}.
|
{keep_state, Data}.
|
||||||
|
|
||||||
call_query(QM0, Id, Index, Ref, Query, QueryOpts) ->
|
-spec call_query(force_sync | async_if_possible, _, _, _, _, _) -> _.
|
||||||
?tp(call_query_enter, #{id => Id, query => Query, query_mode => QM0}),
|
call_query(QM, Id, Index, Ref, Query, QueryOpts) ->
|
||||||
|
?tp(call_query_enter, #{id => Id, query => Query, query_mode => QM}),
|
||||||
case emqx_resource_manager:lookup_cached(Id) of
|
case emqx_resource_manager:lookup_cached(Id) of
|
||||||
{ok, _Group, #{status := stopped}} ->
|
{ok, _Group, #{status := stopped}} ->
|
||||||
?RESOURCE_ERROR(stopped, "resource stopped or disabled");
|
?RESOURCE_ERROR(stopped, "resource stopped or disabled");
|
||||||
{ok, _Group, Resource} ->
|
{ok, _Group, Resource} ->
|
||||||
QM =
|
|
||||||
case QM0 =:= configured of
|
|
||||||
true -> maps:get(query_mode, Resource);
|
|
||||||
false -> QM0
|
|
||||||
end,
|
|
||||||
do_call_query(QM, Id, Index, Ref, Query, QueryOpts, Resource);
|
do_call_query(QM, Id, Index, Ref, Query, QueryOpts, Resource);
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
?RESOURCE_ERROR(not_found, "resource not found")
|
?RESOURCE_ERROR(not_found, "resource not found")
|
||||||
|
@ -1511,9 +1511,9 @@ inc_sent_success(Id, _HasBeenSent = true) ->
|
||||||
inc_sent_success(Id, _HasBeenSent) ->
|
inc_sent_success(Id, _HasBeenSent) ->
|
||||||
emqx_resource_metrics:success_inc(Id).
|
emqx_resource_metrics:success_inc(Id).
|
||||||
|
|
||||||
call_mode(sync, _) -> sync;
|
call_mode(force_sync, _) -> sync;
|
||||||
call_mode(async, always_sync) -> sync;
|
call_mode(async_if_possible, always_sync) -> sync;
|
||||||
call_mode(async, async_if_possible) -> async.
|
call_mode(async_if_possible, async_if_possible) -> async.
|
||||||
|
|
||||||
assert_ok_result(ok) ->
|
assert_ok_result(ok) ->
|
||||||
true;
|
true;
|
||||||
|
@ -1679,6 +1679,17 @@ adjust_batch_time(Id, RequestTimeout, BatchTime0) ->
|
||||||
end,
|
end,
|
||||||
BatchTime.
|
BatchTime.
|
||||||
|
|
||||||
|
%% The request timeout should be greater than the resume interval, as
|
||||||
|
%% it defines how often the buffer worker tries to unblock. If request
|
||||||
|
%% timeout is <= resume interval and the buffer worker is ever
|
||||||
|
%% blocked, than all queued requests will basically fail without being
|
||||||
|
%% attempted.
|
||||||
|
-spec default_resume_interval(request_timeout(), health_check_interval()) -> timer:time().
|
||||||
|
default_resume_interval(_RequestTimeout = infinity, HealthCheckInterval) ->
|
||||||
|
max(1, HealthCheckInterval);
|
||||||
|
default_resume_interval(RequestTimeout, HealthCheckInterval) ->
|
||||||
|
max(1, min(HealthCheckInterval, RequestTimeout div 3)).
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
adjust_batch_time_test_() ->
|
adjust_batch_time_test_() ->
|
||||||
|
|
|
@ -388,6 +388,7 @@ handle_event(state_timeout, health_check, connecting, Data) ->
|
||||||
handle_event(enter, _OldState, connected = State, Data) ->
|
handle_event(enter, _OldState, connected = State, Data) ->
|
||||||
ok = log_state_consistency(State, Data),
|
ok = log_state_consistency(State, Data),
|
||||||
_ = emqx_alarm:deactivate(Data#data.id),
|
_ = emqx_alarm:deactivate(Data#data.id),
|
||||||
|
?tp(resource_connected_enter, #{}),
|
||||||
{keep_state_and_data, health_check_actions(Data)};
|
{keep_state_and_data, health_check_actions(Data)};
|
||||||
handle_event(state_timeout, health_check, connected, Data) ->
|
handle_event(state_timeout, health_check, connected, Data) ->
|
||||||
handle_connected_health_check(Data);
|
handle_connected_health_check(Data);
|
||||||
|
@ -522,7 +523,7 @@ start_resource(Data, From) ->
|
||||||
id => Data#data.id,
|
id => Data#data.id,
|
||||||
reason => Reason
|
reason => Reason
|
||||||
}),
|
}),
|
||||||
_ = maybe_alarm(disconnected, Data#data.id),
|
_ = maybe_alarm(disconnected, Data#data.id, Data#data.error),
|
||||||
%% Keep track of the error reason why the connection did not work
|
%% Keep track of the error reason why the connection did not work
|
||||||
%% so that the Reason can be returned when the verification call is made.
|
%% so that the Reason can be returned when the verification call is made.
|
||||||
UpdatedData = Data#data{status = disconnected, error = Reason},
|
UpdatedData = Data#data{status = disconnected, error = Reason},
|
||||||
|
@ -597,7 +598,7 @@ with_health_check(Data, Func) ->
|
||||||
ResId = Data#data.id,
|
ResId = Data#data.id,
|
||||||
HCRes = emqx_resource:call_health_check(Data#data.manager_id, Data#data.mod, Data#data.state),
|
HCRes = emqx_resource:call_health_check(Data#data.manager_id, Data#data.mod, Data#data.state),
|
||||||
{Status, NewState, Err} = parse_health_check_result(HCRes, Data),
|
{Status, NewState, Err} = parse_health_check_result(HCRes, Data),
|
||||||
_ = maybe_alarm(Status, ResId),
|
_ = maybe_alarm(Status, ResId, Err),
|
||||||
ok = maybe_resume_resource_workers(ResId, Status),
|
ok = maybe_resume_resource_workers(ResId, Status),
|
||||||
UpdatedData = Data#data{
|
UpdatedData = Data#data{
|
||||||
state = NewState, status = Status, error = Err
|
state = NewState, status = Status, error = Err
|
||||||
|
@ -616,15 +617,20 @@ update_state(Data, _DataWas) ->
|
||||||
health_check_interval(Opts) ->
|
health_check_interval(Opts) ->
|
||||||
maps:get(health_check_interval, Opts, ?HEALTHCHECK_INTERVAL).
|
maps:get(health_check_interval, Opts, ?HEALTHCHECK_INTERVAL).
|
||||||
|
|
||||||
maybe_alarm(connected, _ResId) ->
|
maybe_alarm(connected, _ResId, _Error) ->
|
||||||
ok;
|
ok;
|
||||||
maybe_alarm(_Status, <<?TEST_ID_PREFIX, _/binary>>) ->
|
maybe_alarm(_Status, <<?TEST_ID_PREFIX, _/binary>>, _Error) ->
|
||||||
ok;
|
ok;
|
||||||
maybe_alarm(_Status, ResId) ->
|
maybe_alarm(_Status, ResId, Error) ->
|
||||||
|
HrError =
|
||||||
|
case Error of
|
||||||
|
undefined -> <<"Unknown reason">>;
|
||||||
|
_Else -> emqx_misc:readable_error_msg(Error)
|
||||||
|
end,
|
||||||
emqx_alarm:activate(
|
emqx_alarm:activate(
|
||||||
ResId,
|
ResId,
|
||||||
#{resource_id => ResId, reason => resource_down},
|
#{resource_id => ResId, reason => resource_down},
|
||||||
<<"resource down: ", ResId/binary>>
|
<<"resource down: ", HrError/binary>>
|
||||||
).
|
).
|
||||||
|
|
||||||
maybe_resume_resource_workers(ResId, connected) ->
|
maybe_resume_resource_workers(ResId, connected) ->
|
||||||
|
@ -666,6 +672,7 @@ maybe_reply(Actions, From, Reply) ->
|
||||||
data_record_to_external_map(Data) ->
|
data_record_to_external_map(Data) ->
|
||||||
#{
|
#{
|
||||||
id => Data#data.id,
|
id => Data#data.id,
|
||||||
|
error => Data#data.error,
|
||||||
mod => Data#data.mod,
|
mod => Data#data.mod,
|
||||||
callback_mode => Data#data.callback_mode,
|
callback_mode => Data#data.callback_mode,
|
||||||
query_mode => Data#data.query_mode,
|
query_mode => Data#data.query_mode,
|
||||||
|
|
|
@ -55,6 +55,7 @@ fields("creation_opts") ->
|
||||||
[
|
[
|
||||||
{worker_pool_size, fun worker_pool_size/1},
|
{worker_pool_size, fun worker_pool_size/1},
|
||||||
{health_check_interval, fun health_check_interval/1},
|
{health_check_interval, fun health_check_interval/1},
|
||||||
|
{resume_interval, fun resume_interval/1},
|
||||||
{start_after_created, fun start_after_created/1},
|
{start_after_created, fun start_after_created/1},
|
||||||
{start_timeout, fun start_timeout/1},
|
{start_timeout, fun start_timeout/1},
|
||||||
{auto_restart_interval, fun auto_restart_interval/1},
|
{auto_restart_interval, fun auto_restart_interval/1},
|
||||||
|
@ -81,6 +82,12 @@ worker_pool_size(default) -> ?WORKER_POOL_SIZE;
|
||||||
worker_pool_size(required) -> false;
|
worker_pool_size(required) -> false;
|
||||||
worker_pool_size(_) -> undefined.
|
worker_pool_size(_) -> undefined.
|
||||||
|
|
||||||
|
resume_interval(type) -> emqx_schema:duration_ms();
|
||||||
|
resume_interval(hidden) -> true;
|
||||||
|
resume_interval(desc) -> ?DESC("resume_interval");
|
||||||
|
resume_interval(required) -> false;
|
||||||
|
resume_interval(_) -> undefined.
|
||||||
|
|
||||||
health_check_interval(type) -> emqx_schema:duration_ms();
|
health_check_interval(type) -> emqx_schema:duration_ms();
|
||||||
health_check_interval(desc) -> ?DESC("health_check_interval");
|
health_check_interval(desc) -> ?DESC("health_check_interval");
|
||||||
health_check_interval(default) -> ?HEALTHCHECK_INTERVAL_RAW;
|
health_check_interval(default) -> ?HEALTHCHECK_INTERVAL_RAW;
|
||||||
|
|
|
@ -146,6 +146,12 @@ on_query(_InstId, {sleep_before_reply, For}, #{pid := Pid}) ->
|
||||||
{error, timeout}
|
{error, timeout}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
on_query_async(_InstId, block, ReplyFun, #{pid := Pid}) ->
|
||||||
|
Pid ! {block, ReplyFun},
|
||||||
|
{ok, Pid};
|
||||||
|
on_query_async(_InstId, resume, ReplyFun, #{pid := Pid}) ->
|
||||||
|
Pid ! {resume, ReplyFun},
|
||||||
|
{ok, Pid};
|
||||||
on_query_async(_InstId, {inc_counter, N}, ReplyFun, #{pid := Pid}) ->
|
on_query_async(_InstId, {inc_counter, N}, ReplyFun, #{pid := Pid}) ->
|
||||||
Pid ! {inc, N, ReplyFun},
|
Pid ! {inc, N, ReplyFun},
|
||||||
{ok, Pid};
|
{ok, Pid};
|
||||||
|
@ -274,6 +280,10 @@ counter_loop(
|
||||||
block ->
|
block ->
|
||||||
ct:pal("counter recv: ~p", [block]),
|
ct:pal("counter recv: ~p", [block]),
|
||||||
State#{status => blocked};
|
State#{status => blocked};
|
||||||
|
{block, ReplyFun} ->
|
||||||
|
ct:pal("counter recv: ~p", [block]),
|
||||||
|
apply_reply(ReplyFun, ok),
|
||||||
|
State#{status => blocked};
|
||||||
{block_now, ReplyFun} ->
|
{block_now, ReplyFun} ->
|
||||||
ct:pal("counter recv: ~p", [block_now]),
|
ct:pal("counter recv: ~p", [block_now]),
|
||||||
apply_reply(
|
apply_reply(
|
||||||
|
@ -284,6 +294,11 @@ counter_loop(
|
||||||
{messages, Msgs} = erlang:process_info(self(), messages),
|
{messages, Msgs} = erlang:process_info(self(), messages),
|
||||||
ct:pal("counter recv: ~p, buffered msgs: ~p", [resume, length(Msgs)]),
|
ct:pal("counter recv: ~p, buffered msgs: ~p", [resume, length(Msgs)]),
|
||||||
State#{status => running};
|
State#{status => running};
|
||||||
|
{resume, ReplyFun} ->
|
||||||
|
{messages, Msgs} = erlang:process_info(self(), messages),
|
||||||
|
ct:pal("counter recv: ~p, buffered msgs: ~p", [resume, length(Msgs)]),
|
||||||
|
apply_reply(ReplyFun, ok),
|
||||||
|
State#{status => running};
|
||||||
{inc, N, ReplyFun} when Status == running ->
|
{inc, N, ReplyFun} when Status == running ->
|
||||||
%ct:pal("async counter recv: ~p", [{inc, N}]),
|
%ct:pal("async counter recv: ~p", [{inc, N}]),
|
||||||
apply_reply(ReplyFun, ok),
|
apply_reply(ReplyFun, ok),
|
||||||
|
|
|
@ -2561,6 +2561,84 @@ do_t_recursive_flush() ->
|
||||||
),
|
),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
t_call_mode_uncoupled_from_query_mode(_Config) ->
|
||||||
|
DefaultOpts = #{
|
||||||
|
batch_size => 1,
|
||||||
|
batch_time => 5,
|
||||||
|
worker_pool_size => 1
|
||||||
|
},
|
||||||
|
?check_trace(
|
||||||
|
begin
|
||||||
|
%% We check that we can call the buffer workers with async
|
||||||
|
%% calls, even if the underlying connector itself only
|
||||||
|
%% supports sync calls.
|
||||||
|
emqx_connector_demo:set_callback_mode(always_sync),
|
||||||
|
{ok, _} = emqx_resource:create(
|
||||||
|
?ID,
|
||||||
|
?DEFAULT_RESOURCE_GROUP,
|
||||||
|
?TEST_RESOURCE,
|
||||||
|
#{name => test_resource},
|
||||||
|
DefaultOpts#{query_mode => async}
|
||||||
|
),
|
||||||
|
?tp_span(
|
||||||
|
async_query_sync_driver,
|
||||||
|
#{},
|
||||||
|
?assertMatch(
|
||||||
|
{ok, {ok, _}},
|
||||||
|
?wait_async_action(
|
||||||
|
emqx_resource:query(?ID, {inc_counter, 1}),
|
||||||
|
#{?snk_kind := buffer_worker_flush_ack},
|
||||||
|
500
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
?assertEqual(ok, emqx_resource:remove_local(?ID)),
|
||||||
|
|
||||||
|
%% And we check the converse: a connector that allows async
|
||||||
|
%% calls can be called synchronously, but the underlying
|
||||||
|
%% call should be async.
|
||||||
|
emqx_connector_demo:set_callback_mode(async_if_possible),
|
||||||
|
{ok, _} = emqx_resource:create(
|
||||||
|
?ID,
|
||||||
|
?DEFAULT_RESOURCE_GROUP,
|
||||||
|
?TEST_RESOURCE,
|
||||||
|
#{name => test_resource},
|
||||||
|
DefaultOpts#{query_mode => sync}
|
||||||
|
),
|
||||||
|
?tp_span(
|
||||||
|
sync_query_async_driver,
|
||||||
|
#{},
|
||||||
|
?assertEqual(ok, emqx_resource:query(?ID, {inc_counter, 2}))
|
||||||
|
),
|
||||||
|
?assertEqual(ok, emqx_resource:remove_local(?ID)),
|
||||||
|
?tp(sync_query_async_driver, #{}),
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
fun(Trace0) ->
|
||||||
|
Trace1 = trace_between_span(Trace0, async_query_sync_driver),
|
||||||
|
ct:pal("async query calling sync driver\n ~p", [Trace1]),
|
||||||
|
?assert(
|
||||||
|
?strict_causality(
|
||||||
|
#{?snk_kind := async_query, request := {inc_counter, 1}},
|
||||||
|
#{?snk_kind := call_query, call_mode := sync},
|
||||||
|
Trace1
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
Trace2 = trace_between_span(Trace0, sync_query_async_driver),
|
||||||
|
ct:pal("sync query calling async driver\n ~p", [Trace2]),
|
||||||
|
?assert(
|
||||||
|
?strict_causality(
|
||||||
|
#{?snk_kind := sync_query, request := {inc_counter, 2}},
|
||||||
|
#{?snk_kind := call_query_async},
|
||||||
|
Trace2
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
ok
|
||||||
|
end
|
||||||
|
).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Helpers
|
%% Helpers
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
@ -2742,3 +2820,8 @@ assert_async_retry_fail_then_succeed_inflight(Trace) ->
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
trace_between_span(Trace0, Marker) ->
|
||||||
|
{Trace1, [_ | _]} = ?split_trace_at(#{?snk_kind := Marker, ?snk_span := {complete, _}}, Trace0),
|
||||||
|
{[_ | _], [_ | Trace2]} = ?split_trace_at(#{?snk_kind := Marker, ?snk_span := start}, Trace1),
|
||||||
|
Trace2.
|
||||||
|
|
|
@ -1,23 +1,46 @@
|
||||||
|
|
||||||
# emqx-rule-engine
|
# Emqx Rule Engine
|
||||||
|
|
||||||
IoT Rule Engine
|
The rule engine's goal is to provide a simple and flexible way to transform and
|
||||||
|
reroute the messages coming to the EMQX broker. For example, one message
|
||||||
|
containing measurements from multiple sensors of different types can be
|
||||||
|
transformed into multiple messages.
|
||||||
|
|
||||||
|
|
||||||
|
## Concepts
|
||||||
|
|
||||||
|
A rule is quite simple. A rule describes which messages it affects by
|
||||||
|
specifying a topic filter and a set of conditions that need to be met. If a
|
||||||
|
message matches the topic filter and all the conditions are met, the rule is
|
||||||
|
triggered. The rule can then transform the message and route it to a different
|
||||||
|
topic, or send it to another service (defined by an EMQX bridge). The rule
|
||||||
|
engine's message data transformation is designed to work well with structured data
|
||||||
|
such as JSON, avro, and protobuf.
|
||||||
|
|
||||||
|
|
||||||
|
A rule consists of the three parts **MATCH**, **TRANSFORM** and **ACTIONS** that are
|
||||||
|
described below:
|
||||||
|
|
||||||
|
* **MATCH** - The rule's trigger condition. The rule is triggered when a message
|
||||||
|
arrives that matches the topic filter and all the specified conditions are met.
|
||||||
|
* **TRANSFORM** - The rule's data transformation. The rule can select data from the
|
||||||
|
incoming message and transform it into a new message.
|
||||||
|
* **ACTIONS** - The rule's action(s). The rule can have one or more actions. The
|
||||||
|
actions are executed when the rule is triggered. The actions can be to route
|
||||||
|
the message to a different topic, or send it to another service (defined by
|
||||||
|
an EMQX bridge).
|
||||||
|
|
||||||
## Concept
|
|
||||||
|
|
||||||
```
|
|
||||||
iot rule "Rule Name"
|
|
||||||
when
|
|
||||||
match TopicFilters and Conditions
|
|
||||||
select
|
|
||||||
para1 = val1
|
|
||||||
para2 = val2
|
|
||||||
then
|
|
||||||
take action(#{para2 => val1, #para2 => val2})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
The following diagram shows how the rule engine is integrated with the EMQX
|
||||||
|
message broker. Incoming messages are checked against the rules, and if a rule
|
||||||
|
matches, it is triggered with the message as input. The rule can then transform
|
||||||
|
or split the message and/or route it to a different topic, or send it to another
|
||||||
|
service (defined by an EMQX bridge).
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|-----------------|
|
|-----------------|
|
||||||
Pub ---->| Message Routing |----> Sub
|
Pub ---->| Message Routing |----> Sub
|
||||||
|
@ -28,11 +51,33 @@ iot rule "Rule Name"
|
||||||
| Rule Engine |
|
| Rule Engine |
|
||||||
|-----------------|
|
|-----------------|
|
||||||
| |
|
| |
|
||||||
Backends Services Bridges
|
Services Bridges (defined by EMQX bridges)
|
||||||
```
|
```
|
||||||
|
|
||||||
## SQL for Rule query statement
|
## Domain Specific Language for Rules
|
||||||
|
|
||||||
|
The **MATCH** and **TRANSFORM** parts of the rule are specified using a domain
|
||||||
|
specific language that looks similar to SQL. The following is an example of a
|
||||||
|
rule engine statement. The `from "topic/a"` part specifies the topic filter
|
||||||
|
(only messages to the topic `topic/a` will be considered). The `where t > 50`
|
||||||
|
part specifies the condition that needs to be met for the rule to be triggered.
|
||||||
|
The `select id, time, temperature as t` part specifies the data transformation
|
||||||
|
(the selected fields will remain in the transformed message payload). The `as
|
||||||
|
t` part specifies that the `temperature` field name is changed to `t` in the
|
||||||
|
output message. The name `t` can also be used in the where part of the rule as
|
||||||
|
an alias for `t`.
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
select id, time, temperature as t from "topic/a" where t > 50;
|
select id, time, temperature as t from "topic/a" where t > 50
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This just scratches the surface of what is possible with the rule engine. The
|
||||||
|
full documentation is available at [EMQX Rule
|
||||||
|
Engine](https://www.emqx.io/docs/en/v5.0/data-integration/rules.html). For
|
||||||
|
example, there are many built-in functions that can be used in the rule engine
|
||||||
|
language to help in doing transformations and matching. One of the [built-in
|
||||||
|
functions allows you to run JQ
|
||||||
|
queries](https://www.emqx.io/docs/en/v5.0/data-integration/rule-sql-jq.html)
|
||||||
|
which allows you to do complex transformations of the message.
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ schema("/rules") ->
|
||||||
ref(emqx_dashboard_swagger, page),
|
ref(emqx_dashboard_swagger, page),
|
||||||
ref(emqx_dashboard_swagger, limit)
|
ref(emqx_dashboard_swagger, limit)
|
||||||
],
|
],
|
||||||
summary => <<"List Rules">>,
|
summary => <<"List rules">>,
|
||||||
responses => #{
|
responses => #{
|
||||||
200 =>
|
200 =>
|
||||||
[
|
[
|
||||||
|
@ -193,7 +193,7 @@ schema("/rules") ->
|
||||||
post => #{
|
post => #{
|
||||||
tags => [<<"rules">>],
|
tags => [<<"rules">>],
|
||||||
description => ?DESC("api2"),
|
description => ?DESC("api2"),
|
||||||
summary => <<"Create a Rule">>,
|
summary => <<"Create a rule">>,
|
||||||
'requestBody' => rule_creation_schema(),
|
'requestBody' => rule_creation_schema(),
|
||||||
responses => #{
|
responses => #{
|
||||||
400 => error_schema('BAD_REQUEST', "Invalid Parameters"),
|
400 => error_schema('BAD_REQUEST', "Invalid Parameters"),
|
||||||
|
@ -207,7 +207,7 @@ schema("/rule_events") ->
|
||||||
get => #{
|
get => #{
|
||||||
tags => [<<"rules">>],
|
tags => [<<"rules">>],
|
||||||
description => ?DESC("api3"),
|
description => ?DESC("api3"),
|
||||||
summary => <<"List Events">>,
|
summary => <<"List rule events">>,
|
||||||
responses => #{
|
responses => #{
|
||||||
200 => mk(ref(emqx_rule_api_schema, "rule_events"), #{})
|
200 => mk(ref(emqx_rule_api_schema, "rule_events"), #{})
|
||||||
}
|
}
|
||||||
|
@ -219,7 +219,7 @@ schema("/rules/:id") ->
|
||||||
get => #{
|
get => #{
|
||||||
tags => [<<"rules">>],
|
tags => [<<"rules">>],
|
||||||
description => ?DESC("api4"),
|
description => ?DESC("api4"),
|
||||||
summary => <<"Get a Rule">>,
|
summary => <<"Get rule">>,
|
||||||
parameters => param_path_id(),
|
parameters => param_path_id(),
|
||||||
responses => #{
|
responses => #{
|
||||||
404 => error_schema('NOT_FOUND', "Rule not found"),
|
404 => error_schema('NOT_FOUND', "Rule not found"),
|
||||||
|
@ -229,7 +229,7 @@ schema("/rules/:id") ->
|
||||||
put => #{
|
put => #{
|
||||||
tags => [<<"rules">>],
|
tags => [<<"rules">>],
|
||||||
description => ?DESC("api5"),
|
description => ?DESC("api5"),
|
||||||
summary => <<"Update a Rule">>,
|
summary => <<"Update rule">>,
|
||||||
parameters => param_path_id(),
|
parameters => param_path_id(),
|
||||||
'requestBody' => rule_creation_schema(),
|
'requestBody' => rule_creation_schema(),
|
||||||
responses => #{
|
responses => #{
|
||||||
|
@ -240,7 +240,7 @@ schema("/rules/:id") ->
|
||||||
delete => #{
|
delete => #{
|
||||||
tags => [<<"rules">>],
|
tags => [<<"rules">>],
|
||||||
description => ?DESC("api6"),
|
description => ?DESC("api6"),
|
||||||
summary => <<"Delete a Rule">>,
|
summary => <<"Delete rule">>,
|
||||||
parameters => param_path_id(),
|
parameters => param_path_id(),
|
||||||
responses => #{
|
responses => #{
|
||||||
204 => <<"Delete rule successfully">>
|
204 => <<"Delete rule successfully">>
|
||||||
|
@ -253,7 +253,7 @@ schema("/rules/:id/metrics") ->
|
||||||
get => #{
|
get => #{
|
||||||
tags => [<<"rules">>],
|
tags => [<<"rules">>],
|
||||||
description => ?DESC("api4_1"),
|
description => ?DESC("api4_1"),
|
||||||
summary => <<"Get a Rule's Metrics">>,
|
summary => <<"Get rule metrics">>,
|
||||||
parameters => param_path_id(),
|
parameters => param_path_id(),
|
||||||
responses => #{
|
responses => #{
|
||||||
404 => error_schema('NOT_FOUND', "Rule not found"),
|
404 => error_schema('NOT_FOUND', "Rule not found"),
|
||||||
|
@ -267,7 +267,7 @@ schema("/rules/:id/metrics/reset") ->
|
||||||
put => #{
|
put => #{
|
||||||
tags => [<<"rules">>],
|
tags => [<<"rules">>],
|
||||||
description => ?DESC("api7"),
|
description => ?DESC("api7"),
|
||||||
summary => <<"Reset a Rule Metrics">>,
|
summary => <<"Reset rule metrics">>,
|
||||||
parameters => param_path_id(),
|
parameters => param_path_id(),
|
||||||
responses => #{
|
responses => #{
|
||||||
404 => error_schema('NOT_FOUND', "Rule not found"),
|
404 => error_schema('NOT_FOUND', "Rule not found"),
|
||||||
|
@ -281,7 +281,7 @@ schema("/rule_test") ->
|
||||||
post => #{
|
post => #{
|
||||||
tags => [<<"rules">>],
|
tags => [<<"rules">>],
|
||||||
description => ?DESC("api8"),
|
description => ?DESC("api8"),
|
||||||
summary => <<"Test a Rule">>,
|
summary => <<"Test a rule">>,
|
||||||
'requestBody' => rule_test_schema(),
|
'requestBody' => rule_test_schema(),
|
||||||
responses => #{
|
responses => #{
|
||||||
400 => error_schema('BAD_REQUEST', "Invalid Parameters"),
|
400 => error_schema('BAD_REQUEST', "Invalid Parameters"),
|
||||||
|
|
25
build
25
build
|
@ -147,7 +147,7 @@ make_rel() {
|
||||||
|
|
||||||
make_elixir_rel() {
|
make_elixir_rel() {
|
||||||
./scripts/pre-compile.sh "$PROFILE"
|
./scripts/pre-compile.sh "$PROFILE"
|
||||||
export_release_vars "$PROFILE"
|
export_elixir_release_vars "$PROFILE"
|
||||||
# for some reason, this has to be run outside "do"...
|
# for some reason, this has to be run outside "do"...
|
||||||
mix local.rebar --if-missing --force
|
mix local.rebar --if-missing --force
|
||||||
# shellcheck disable=SC1010
|
# shellcheck disable=SC1010
|
||||||
|
@ -362,7 +362,7 @@ function join {
|
||||||
|
|
||||||
# used to control the Elixir Mix Release output
|
# used to control the Elixir Mix Release output
|
||||||
# see docstring in `mix.exs`
|
# see docstring in `mix.exs`
|
||||||
export_release_vars() {
|
export_elixir_release_vars() {
|
||||||
local profile="$1"
|
local profile="$1"
|
||||||
case "$profile" in
|
case "$profile" in
|
||||||
emqx|emqx-enterprise)
|
emqx|emqx-enterprise)
|
||||||
|
@ -376,27 +376,6 @@ export_release_vars() {
|
||||||
exit 1
|
exit 1
|
||||||
esac
|
esac
|
||||||
export MIX_ENV="$profile"
|
export MIX_ENV="$profile"
|
||||||
|
|
||||||
local erl_opts=()
|
|
||||||
|
|
||||||
case "$(is_enterprise "$profile")" in
|
|
||||||
'yes')
|
|
||||||
erl_opts+=( "{d, 'EMQX_RELEASE_EDITION', ee}" )
|
|
||||||
;;
|
|
||||||
'no')
|
|
||||||
erl_opts+=( "{d, 'EMQX_RELEASE_EDITION', ce}" )
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# At this time, Mix provides no easy way to pass `erl_opts' to
|
|
||||||
# dependencies. The workaround is to set this variable before
|
|
||||||
# compiling the project, so that `emqx_release.erl' picks up
|
|
||||||
# `emqx_vsn' as if it was compiled by rebar3.
|
|
||||||
erl_opts+=( "{compile_info,[{emqx_vsn,\"${PKG_VSN}\"}]}" )
|
|
||||||
erl_opts+=( "{d,snk_kind,msg}" )
|
|
||||||
|
|
||||||
ERL_COMPILER_OPTIONS="[$(join , "${erl_opts[@]}")]"
|
|
||||||
export ERL_COMPILER_OPTIONS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log "building artifact=$ARTIFACT for profile=$PROFILE"
|
log "building artifact=$ARTIFACT for profile=$PROFILE"
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Add low level tuning settings for QUIC listeners.
|
|
|
@ -1 +0,0 @@
|
||||||
为 QUIC 监听器添加更多底层调优选项。
|
|
|
@ -1 +0,0 @@
|
||||||
Start releasing Rocky Linux 9 (compatible with Enterprise Linux 9) and MacOS 12 packages
|
|
|
@ -1 +0,0 @@
|
||||||
Errors returned by rule engine API are formatted in a more human readable way rather than dumping the raw error including the stacktrace.
|
|
|
@ -1 +0,0 @@
|
||||||
规则引擎 API 返回用户可读的错误信息而不是原始的栈追踪信息。
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue