Compare commits
212 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01dbf8e50a | ||
|
|
58d0302c74 | ||
|
|
90ea6a13de | ||
|
|
4f80a7979f | ||
|
|
3812101c25 | ||
|
|
8c79c8b32d | ||
|
|
0afc437252 | ||
|
|
6ae53fea6e | ||
|
|
d925ccf45a | ||
|
|
24243a4b4d | ||
|
|
ef9ebc29b7 | ||
|
|
bd4e0f2b28 | ||
|
|
e952c9f85a | ||
|
|
df67e9a0bd | ||
|
|
7e47ed0879 | ||
|
|
1952ec1527 | ||
|
|
9ca5205d6a | ||
|
|
8a8da9d162 | ||
|
|
6440c71492 | ||
|
|
0b66b2debb | ||
|
|
351c142b91 | ||
|
|
8ce6aed66f | ||
|
|
778d18d816 | ||
|
|
2ea436bbbc | ||
|
|
b5a820b6f4 | ||
|
|
37f5043493 | ||
|
|
7638fb75a3 | ||
|
|
fdad6d3b7b | ||
|
|
074e300974 | ||
|
|
523e068195 | ||
|
|
9a7c617311 | ||
|
|
c214899b75 | ||
|
|
e31a8c8064 | ||
|
|
31cc27df1b | ||
|
|
d60051b5cb | ||
|
|
6770ae2a99 | ||
|
|
84738809ef | ||
|
|
43a08f7eef | ||
|
|
89aeec5f97 | ||
|
|
00ee5415a5 | ||
|
|
b5bd79b61e | ||
|
|
7080c236eb | ||
|
|
bb073eb51e | ||
|
|
7372d49aca | ||
|
|
1d771d4a0a | ||
|
|
dcffdbd98e | ||
|
|
abcc2e4125 | ||
|
|
e453381382 | ||
|
|
9a9913998a | ||
|
|
a3cdb66fe6 | ||
|
|
39aa84e2fd | ||
|
|
1ec2270eb5 | ||
|
|
5d1ee9b538 | ||
|
|
03d3cf57e9 | ||
|
|
2a5465ddb0 | ||
|
|
84efc786d4 | ||
|
|
12e5fb6f7f | ||
|
|
5c66900fdb | ||
|
|
2710ead089 | ||
|
|
839aa420e0 | ||
|
|
4ee42d4092 | ||
|
|
b2950f0718 | ||
|
|
7b1793476d | ||
|
|
f8750ef231 | ||
|
|
cc9283530f | ||
|
|
5a61e56766 | ||
|
|
8f71d514ee | ||
|
|
1b122b228f | ||
|
|
e88c0cdd38 | ||
|
|
3ceb188b81 | ||
|
|
0bc52ecb3c | ||
|
|
36d6c37df3 | ||
|
|
8fd793fdab | ||
|
|
be1909aedb | ||
|
|
e83b4d84ec | ||
|
|
b39b3f38fe | ||
|
|
d50765925b | ||
|
|
e0f8971155 | ||
|
|
f1d53f5610 | ||
|
|
4b4e6b1c72 | ||
|
|
095af716c9 | ||
|
|
e4e9f050df | ||
|
|
638b9b9428 | ||
|
|
0d4d18c595 | ||
|
|
33350c168d | ||
|
|
1bbb882225 | ||
|
|
6e86e8e5c5 | ||
|
|
7cb4c8793a | ||
|
|
4d2c636d9b | ||
|
|
9895d6de43 | ||
|
|
f62b6f94c5 | ||
|
|
e00d9a6c59 | ||
|
|
d2e573bbed | ||
|
|
8c0b6ee70d | ||
|
|
b9c7b1f559 | ||
|
|
7b5b876b0c | ||
|
|
848fb50132 | ||
|
|
6206402cae | ||
|
|
b24dc91ef2 | ||
|
|
d2d58f9058 | ||
|
|
e6fb75f715 | ||
|
|
3b31049636 | ||
|
|
5db091d800 | ||
|
|
8278ecb6a2 | ||
|
|
084eeba24a | ||
|
|
0134840300 | ||
|
|
05335aaf3f | ||
|
|
fb38741036 | ||
|
|
5a07feaffd | ||
|
|
e555c287bf | ||
|
|
ad52f32a3b | ||
|
|
665b27cfbe | ||
|
|
871d558ffd | ||
|
|
881e00e7e3 | ||
|
|
29963b9d75 | ||
|
|
bea8ec2d98 | ||
|
|
851f5fad58 | ||
|
|
1125837420 | ||
|
|
34f9a13da3 | ||
|
|
1432ab4b52 | ||
|
|
8f3a8e3572 | ||
|
|
87d5903cfc | ||
|
|
a50639af23 | ||
|
|
c4b1a70dac | ||
|
|
94677c7a39 | ||
|
|
cd2295ef05 | ||
|
|
944bb42074 | ||
|
|
1350364804 | ||
|
|
3625395d32 | ||
|
|
c32ed90a2e | ||
|
|
9af8c00bca | ||
|
|
0af476ac8e | ||
|
|
3b58e8ce3c | ||
|
|
1fffb80066 | ||
|
|
5f7e6e69e3 | ||
|
|
14e84b23d1 | ||
|
|
562576b8d4 | ||
|
|
532962527f | ||
|
|
99d1914542 | ||
|
|
6fa534dac1 | ||
|
|
3b6da33535 | ||
|
|
ded4a22a67 | ||
|
|
ab0589916f | ||
|
|
3226690d70 | ||
|
|
4d235491f2 | ||
|
|
96b7890404 | ||
|
|
760cc01217 | ||
|
|
c394a05fc1 | ||
|
|
11ab4483e0 | ||
|
|
65747a7f28 | ||
|
|
ca9ddc4222 | ||
|
|
6a2237b513 | ||
|
|
4afccfad8d | ||
|
|
9a32a1d638 | ||
|
|
e091914a06 | ||
|
|
7b584668ac | ||
|
|
30019327da | ||
|
|
fc8eeeaf0a | ||
|
|
11201881c0 | ||
|
|
498dcb8bcb | ||
|
|
5564239bea | ||
|
|
5319c5f53f | ||
|
|
49b9e1ce1f | ||
|
|
82a61aef09 | ||
|
|
3c58a8f091 | ||
|
|
4a62b80e75 | ||
|
|
52c1687d83 | ||
|
|
238a0f46e0 | ||
|
|
bbf5d2d178 | ||
|
|
1c4ea24da1 | ||
|
|
f277460b9d | ||
|
|
15c1643d34 | ||
|
|
e7f0a05e1a | ||
|
|
8506b5d5a5 | ||
|
|
0d629127a9 | ||
|
|
d6d8d2148d | ||
|
|
5a3f2004bc | ||
|
|
0e86fc8094 | ||
|
|
3c6c4b88ef | ||
|
|
4b4a10be7e | ||
|
|
4f1491858b | ||
|
|
33433b1aaf | ||
|
|
d85ebe0d34 | ||
|
|
235bd85510 | ||
|
|
5c29345f48 | ||
|
|
ebcb2b84ae | ||
|
|
080021e73d | ||
|
|
f447e28c86 | ||
|
|
f3b2ca644c | ||
|
|
4fdead0add | ||
|
|
059c6e9df7 | ||
|
|
a3f0efa8bc | ||
|
|
a79ad5abeb | ||
|
|
c6f8a14643 | ||
|
|
d262357c87 | ||
|
|
5da3730015 | ||
|
|
b444a4887b | ||
|
|
cc7133927f | ||
|
|
4b01042a4f | ||
|
|
4b94ef998d | ||
|
|
db5764b3d7 | ||
|
|
3c0115b71c | ||
|
|
c401c790bc | ||
|
|
271dcacf19 | ||
|
|
983d29d033 | ||
|
|
078f6e1cc4 | ||
|
|
9f8f6bc814 | ||
|
|
ef35178396 | ||
|
|
b3781c6d8f | ||
|
|
2319739f6d | ||
|
|
f9ac1ca698 | ||
|
|
9802bf2f37 |
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
||||
dist
|
||||
bin
|
||||
13
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
13
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -17,7 +17,9 @@ body:
|
||||
id: adguardhome-sync-version
|
||||
attributes:
|
||||
label: AdguardHome-Sync Version
|
||||
description: What version of adguardhome-sync was running when you discovered this issue?
|
||||
description: |
|
||||
- What version of adguardhome-sync was running when you discovered this issue?
|
||||
- Are you running the docker or binary version?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@@ -27,6 +29,15 @@ body:
|
||||
description: What version of adguardhome was running when you discovered this issue?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: os-information
|
||||
attributes:
|
||||
label: OS Information
|
||||
description: |
|
||||
- What Operating System are you running? `cat /etc/os-release`
|
||||
- What is the architecture of your CPU? `uname -m`
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: config
|
||||
attributes:
|
||||
|
||||
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
@@ -1,15 +0,0 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
10
.github/workflows/codeql-analysis.yml
vendored
10
.github/workflows/codeql-analysis.yml
vendored
@@ -20,6 +20,10 @@ on:
|
||||
schedule:
|
||||
- cron: '32 19 * * 6'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
@@ -40,6 +44,12 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
name: docker-image
|
||||
name: docker-images
|
||||
|
||||
on:
|
||||
workflow_dispatch: # allows manual triggering
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
#pull_request:
|
||||
# branches:
|
||||
# - main
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
images:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -44,16 +51,16 @@ jobs:
|
||||
run: |
|
||||
sed -i -e "s|FROM scratch|FROM ${{ matrix.build.fromImage }}|g" Dockerfile
|
||||
|
||||
- name: Build and push ${{github.event.release.tag_name }}
|
||||
- name: Build images ${{github.event.release.tag_name }}
|
||||
id: docker_build_release
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
if: ${{ github.event.release.tag_name != '' }}
|
||||
with:
|
||||
context: .
|
||||
pull: true
|
||||
push: true
|
||||
tags: quay.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}latest,quay.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}${{ github.event.release.tag_name }},ghcr.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}latest,ghcr.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}${{ github.event.release.tag_name }}
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/arm64,linux/ppc64le
|
||||
provenance: false
|
||||
build-args: |
|
||||
VERSION=${{ github.event.release.tag_name }}
|
||||
@@ -63,16 +70,16 @@ jobs:
|
||||
run: echo "NEW_COMMIT_COUNT=$(git log --oneline --since '24 hours ago' | wc -l)" >> $GITHUB_ENV
|
||||
if: ${{ github.event.release.tag_name == '' }}
|
||||
|
||||
- name: Build and push main
|
||||
- name: Build images
|
||||
id: docker_build_main
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
if: ${{ github.event.release.tag_name == '' && env.NEW_COMMIT_COUNT > 0 }}
|
||||
with:
|
||||
context: .
|
||||
pull: true
|
||||
push: true
|
||||
push: ${{ github.ref == 'refs/heads/main' }}
|
||||
tags: quay.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}main,ghcr.io/bakito/adguardhome-sync:${{ matrix.build.tagPrefix }}main
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/arm64,linux/ppc64le
|
||||
provenance: false
|
||||
build-args: |
|
||||
VERSION=main
|
||||
@@ -80,4 +87,3 @@ jobs:
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
|
||||
21
.github/workflows/e2e.yaml
vendored
21
.github/workflows/e2e.yaml
vendored
@@ -8,6 +8,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
e2e:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -15,11 +19,16 @@ jobs:
|
||||
matrix:
|
||||
build:
|
||||
- mode: env
|
||||
protocol: https
|
||||
- mode: file
|
||||
protocol: http
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup jq
|
||||
uses: dcarbone/install-jq-action@v3
|
||||
|
||||
- name: Install kind with registry
|
||||
uses: bakito/kind-with-registry-action@main
|
||||
|
||||
@@ -28,13 +37,19 @@ jobs:
|
||||
|
||||
- name: Install Helm Chart
|
||||
run: ./testdata/e2e/bin/install-chart.sh ${{ matrix.build.mode }}
|
||||
- name: Wait for pod to start
|
||||
run: ./testdata/e2e/bin/wait-for-start.sh ${{ matrix.build.protocol }}
|
||||
- name: Show origin pre Logs
|
||||
run: ./testdata/e2e/bin/show-origin-logs.sh pre
|
||||
- name: Wait for sync to finish
|
||||
run: ./testdata/e2e/bin/wait-for-sync.sh
|
||||
- name: Show origin Logs
|
||||
run: ./testdata/e2e/bin/show-origin-logs.sh
|
||||
run: ./testdata/e2e/bin/wait-for-sync.sh ${{ matrix.build.protocol }}
|
||||
- name: Show origin post Logs
|
||||
run: ./testdata/e2e/bin/show-origin-logs.sh post
|
||||
- name: Show Replica Logs
|
||||
run: ./testdata/e2e/bin/show-replica-logs.sh
|
||||
- name: Show Sync Logs
|
||||
run: ./testdata/e2e/bin/show-sync-logs.sh
|
||||
- name: Show Sync Metrics
|
||||
run: ./testdata/e2e/bin/show-sync-metrics.sh ${{ matrix.build.protocol }}
|
||||
- name: Read latest replica config
|
||||
run: ./testdata/e2e/bin/read-latest-replica-config.sh
|
||||
|
||||
16
.github/workflows/go.yml
vendored
16
.github/workflows/go.yml
vendored
@@ -6,6 +6,10 @@ on:
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
@@ -19,9 +23,9 @@ jobs:
|
||||
go-version-file: "go.mod"
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
skip-pkg-cache: true
|
||||
skip-cache: true
|
||||
|
||||
test:
|
||||
name: test
|
||||
@@ -36,6 +40,9 @@ jobs:
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
|
||||
- name: Model
|
||||
run: make model
|
||||
|
||||
- name: Test
|
||||
run: make test-ci
|
||||
|
||||
@@ -58,7 +65,4 @@ jobs:
|
||||
go-version-file: "go.mod"
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
version: latest
|
||||
args: --skip-publish --snapshot --rm-dist
|
||||
run: make test-release
|
||||
|
||||
17
.github/workflows/stale.yml
vendored
Normal file
17
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue has been inactive for 60 days. If the issue is still relevant please comment to re-activate the issue. If no action is taken within 7 days, the issue will be marked closed.'
|
||||
stale-pr-message: 'This pull request has been inactive for 60 days. If the pull request is still relevant please comment to re-activate the pull request. If no action is taken within 7 days, the pull request will be marked closed.'
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
.vscode
|
||||
.idea
|
||||
.fleet
|
||||
.run
|
||||
coverage.out*
|
||||
dist
|
||||
@@ -8,5 +9,7 @@ main
|
||||
.adguardhome-sync.yaml
|
||||
tmp
|
||||
bin
|
||||
!testdata/e2e/bin
|
||||
config*.yaml
|
||||
*.log
|
||||
wiki
|
||||
|
||||
@@ -3,34 +3,34 @@ run:
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- asciicheck
|
||||
- bodyclose
|
||||
- dogsled
|
||||
- durationcheck
|
||||
- errcheck
|
||||
- errorlint
|
||||
- exportloopref
|
||||
- gci
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goimports
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- importas
|
||||
- ineffassign
|
||||
- megacheck
|
||||
- misspell
|
||||
- nakedret
|
||||
- nolintlint
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- asciicheck
|
||||
- bodyclose
|
||||
- dogsled
|
||||
- durationcheck
|
||||
- errcheck
|
||||
- errorlint
|
||||
- gci
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goimports
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- importas
|
||||
- ineffassign
|
||||
- misspell
|
||||
- nakedret
|
||||
- nolintlint
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
linters-settings:
|
||||
gosec:
|
||||
# Exclude generated files
|
||||
exclude-generated: true
|
||||
excludes:
|
||||
- G601 # not applicable in go 1.22 anymore
|
||||
gofmt:
|
||||
# simplify code: gofmt with `-s` option, true by default
|
||||
simplify: true
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
version: 2
|
||||
# This is an example goreleaser.yaml file with some sane defaults.
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
builds:
|
||||
@@ -30,16 +31,17 @@ builds:
|
||||
hooks:
|
||||
post:
|
||||
# don't upx windows binaries as they make trouble with virus scanners
|
||||
- bash -c 'if [[ "{{ .Path }}" != *.exe ]]; then upx {{ .Path }}; fi'
|
||||
- bash -c 'if [[ "{{ .Path }}" != *.exe ]] && [[ "{{ .Path }}" != *darwin* ]]; then upx {{ .Path }}; fi'
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ .Tag }}-next"
|
||||
version_template: "{{ .Tag }}-next"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^chore'
|
||||
release:
|
||||
prerelease: auto
|
||||
|
||||
104
.toolbox.mk
Normal file
104
.toolbox.mk
Normal file
@@ -0,0 +1,104 @@
|
||||
## toolbox - start
|
||||
## Generated with https://github.com/bakito/toolbox
|
||||
|
||||
## Current working directory
|
||||
TB_LOCALDIR ?= $(shell which cygpath > /dev/null 2>&1 && cygpath -m $$(pwd) || pwd)
|
||||
## Location to install dependencies to
|
||||
TB_LOCALBIN ?= $(TB_LOCALDIR)/bin
|
||||
$(TB_LOCALBIN):
|
||||
mkdir -p $(TB_LOCALBIN)
|
||||
|
||||
## Tool Binaries
|
||||
TB_DEEPCOPY_GEN ?= $(TB_LOCALBIN)/deepcopy-gen
|
||||
TB_GINKGO ?= $(TB_LOCALBIN)/ginkgo
|
||||
TB_GOFUMPT ?= $(TB_LOCALBIN)/gofumpt
|
||||
TB_GOLANGCI_LINT ?= $(TB_LOCALBIN)/golangci-lint
|
||||
TB_GOLINES ?= $(TB_LOCALBIN)/golines
|
||||
TB_GORELEASER ?= $(TB_LOCALBIN)/goreleaser
|
||||
TB_MOCKGEN ?= $(TB_LOCALBIN)/mockgen
|
||||
TB_OAPI_CODEGEN ?= $(TB_LOCALBIN)/oapi-codegen
|
||||
TB_SEMVER ?= $(TB_LOCALBIN)/semver
|
||||
|
||||
## Tool Versions
|
||||
# renovate: packageName=k8s.io/code-generator/cmd/deepcopy-gen
|
||||
TB_DEEPCOPY_GEN_VERSION ?= v0.32.2
|
||||
# renovate: packageName=mvdan.cc/gofumpt
|
||||
TB_GOFUMPT_VERSION ?= v0.7.0
|
||||
# renovate: packageName=github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||
TB_GOLANGCI_LINT_VERSION ?= v1.64.5
|
||||
# renovate: packageName=github.com/segmentio/golines
|
||||
TB_GOLINES_VERSION ?= v0.12.2
|
||||
# renovate: packageName=github.com/goreleaser/goreleaser/v2
|
||||
TB_GORELEASER_VERSION ?= v2.7.0
|
||||
# renovate: packageName=go.uber.org/mock/mockgen
|
||||
TB_MOCKGEN_VERSION ?= v0.5.0
|
||||
# renovate: packageName=github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen
|
||||
TB_OAPI_CODEGEN_VERSION ?= v2.4.1
|
||||
# renovate: packageName=github.com/bakito/semver
|
||||
TB_SEMVER_VERSION ?= v1.1.3
|
||||
|
||||
## Tool Installer
|
||||
.PHONY: tb.deepcopy-gen
|
||||
tb.deepcopy-gen: $(TB_DEEPCOPY_GEN) ## Download deepcopy-gen locally if necessary.
|
||||
$(TB_DEEPCOPY_GEN): $(TB_LOCALBIN)
|
||||
test -s $(TB_LOCALBIN)/deepcopy-gen || GOBIN=$(TB_LOCALBIN) go install k8s.io/code-generator/cmd/deepcopy-gen@$(TB_DEEPCOPY_GEN_VERSION)
|
||||
.PHONY: tb.ginkgo
|
||||
tb.ginkgo: $(TB_GINKGO) ## Download ginkgo locally if necessary.
|
||||
$(TB_GINKGO): $(TB_LOCALBIN)
|
||||
test -s $(TB_LOCALBIN)/ginkgo || GOBIN=$(TB_LOCALBIN) go install github.com/onsi/ginkgo/v2/ginkgo
|
||||
.PHONY: tb.gofumpt
|
||||
tb.gofumpt: $(TB_GOFUMPT) ## Download gofumpt locally if necessary.
|
||||
$(TB_GOFUMPT): $(TB_LOCALBIN)
|
||||
test -s $(TB_LOCALBIN)/gofumpt || GOBIN=$(TB_LOCALBIN) go install mvdan.cc/gofumpt@$(TB_GOFUMPT_VERSION)
|
||||
.PHONY: tb.golangci-lint
|
||||
tb.golangci-lint: $(TB_GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
|
||||
$(TB_GOLANGCI_LINT): $(TB_LOCALBIN)
|
||||
test -s $(TB_LOCALBIN)/golangci-lint || GOBIN=$(TB_LOCALBIN) go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(TB_GOLANGCI_LINT_VERSION)
|
||||
.PHONY: tb.golines
|
||||
tb.golines: $(TB_GOLINES) ## Download golines locally if necessary.
|
||||
$(TB_GOLINES): $(TB_LOCALBIN)
|
||||
test -s $(TB_LOCALBIN)/golines || GOBIN=$(TB_LOCALBIN) go install github.com/segmentio/golines@$(TB_GOLINES_VERSION)
|
||||
.PHONY: tb.goreleaser
|
||||
tb.goreleaser: $(TB_GORELEASER) ## Download goreleaser locally if necessary.
|
||||
$(TB_GORELEASER): $(TB_LOCALBIN)
|
||||
test -s $(TB_LOCALBIN)/goreleaser || GOBIN=$(TB_LOCALBIN) go install github.com/goreleaser/goreleaser/v2@$(TB_GORELEASER_VERSION)
|
||||
.PHONY: tb.mockgen
|
||||
tb.mockgen: $(TB_MOCKGEN) ## Download mockgen locally if necessary.
|
||||
$(TB_MOCKGEN): $(TB_LOCALBIN)
|
||||
test -s $(TB_LOCALBIN)/mockgen || GOBIN=$(TB_LOCALBIN) go install go.uber.org/mock/mockgen@$(TB_MOCKGEN_VERSION)
|
||||
.PHONY: tb.oapi-codegen
|
||||
tb.oapi-codegen: $(TB_OAPI_CODEGEN) ## Download oapi-codegen locally if necessary.
|
||||
$(TB_OAPI_CODEGEN): $(TB_LOCALBIN)
|
||||
test -s $(TB_LOCALBIN)/oapi-codegen || GOBIN=$(TB_LOCALBIN) go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@$(TB_OAPI_CODEGEN_VERSION)
|
||||
.PHONY: tb.semver
|
||||
tb.semver: $(TB_SEMVER) ## Download semver locally if necessary.
|
||||
$(TB_SEMVER): $(TB_LOCALBIN)
|
||||
test -s $(TB_LOCALBIN)/semver || GOBIN=$(TB_LOCALBIN) go install github.com/bakito/semver@$(TB_SEMVER_VERSION)
|
||||
|
||||
## Reset Tools
|
||||
.PHONY: tb.reset
|
||||
tb.reset:
|
||||
@rm -f \
|
||||
$(TB_LOCALBIN)/deepcopy-gen \
|
||||
$(TB_LOCALBIN)/ginkgo \
|
||||
$(TB_LOCALBIN)/gofumpt \
|
||||
$(TB_LOCALBIN)/golangci-lint \
|
||||
$(TB_LOCALBIN)/golines \
|
||||
$(TB_LOCALBIN)/goreleaser \
|
||||
$(TB_LOCALBIN)/mockgen \
|
||||
$(TB_LOCALBIN)/oapi-codegen \
|
||||
$(TB_LOCALBIN)/semver
|
||||
|
||||
## Update Tools
|
||||
.PHONY: tb.update
|
||||
tb.update: tb.reset
|
||||
toolbox makefile --renovate -f $(TB_LOCALDIR)/Makefile \
|
||||
k8s.io/code-generator/cmd/deepcopy-gen@github.com/kubernetes/code-generator \
|
||||
mvdan.cc/gofumpt@github.com/mvdan/gofumpt \
|
||||
github.com/golangci/golangci-lint/cmd/golangci-lint \
|
||||
github.com/segmentio/golines \
|
||||
github.com/goreleaser/goreleaser/v2 \
|
||||
go.uber.org/mock/mockgen@github.com/uber-go/mock \
|
||||
github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen \
|
||||
github.com/bakito/semver
|
||||
## toolbox - end
|
||||
12
Dockerfile
12
Dockerfile
@@ -1,10 +1,8 @@
|
||||
FROM golang:1.21-bullseye as builder
|
||||
FROM golang:1.24-alpine AS builder
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y upx ca-certificates tzdata && \
|
||||
apt-get upgrade -y # upgrade to get latest ca-certs
|
||||
RUN apk update && apk add upx ca-certificates tzdata
|
||||
|
||||
ARG VERSION=main
|
||||
ARG BUILD="N/A"
|
||||
@@ -15,8 +13,10 @@ ENV GO111MODULE=on \
|
||||
|
||||
COPY . /go/src/app/
|
||||
|
||||
RUN go build -a -installsuffix cgo -ldflags="-w -s -X github.com/bakito/adguardhome-sync/version.Version=${VERSION} -X github.com/bakito/adguardhome-sync/version.Build=${BUILD}" -o adguardhome-sync . \
|
||||
&& upx -q adguardhome-sync
|
||||
|
||||
RUN go build -a -installsuffix cgo -ldflags="-w -s -X github.com/bakito/adguardhome-sync/version.Version=${VERSION} -X github.com/bakito/adguardhome-sync/version.Build=${BUILD}" -o adguardhome-sync .
|
||||
|
||||
RUN go version && upx -q adguardhome-sync
|
||||
|
||||
# application image
|
||||
FROM scratch
|
||||
|
||||
116
Makefile
116
Makefile
@@ -1,105 +1,42 @@
|
||||
# Include toolbox tasks
|
||||
include ./.toolbox.mk
|
||||
|
||||
# Run go lint against code
|
||||
lint: golangci-lint
|
||||
$(GOLANGCI_LINT) run --fix
|
||||
lint: tb.golangci-lint
|
||||
$(TB_GOLANGCI_LINT) run --fix
|
||||
|
||||
# Run go mod tidy
|
||||
tidy:
|
||||
go mod tidy
|
||||
|
||||
generate: deepcopy-gen
|
||||
generate: tb.deepcopy-gen
|
||||
@mkdir -p ./tmp
|
||||
@touch ./tmp/deepcopy-gen-boilerplate.go.txt
|
||||
$(DEEPCOPY_GEN) -h ./tmp/deepcopy-gen-boilerplate.go.txt -i ./pkg/types
|
||||
$(TB_DEEPCOPY_GEN) --go-header-file ./tmp/deepcopy-gen-boilerplate.go.txt --bounding-dirs ./pkg/types
|
||||
|
||||
fmt: tb.golines tb.gofumpt
|
||||
$(TB_GOLINES) --base-formatter="$(TB_GOFUMPT)" --max-len=120 --write-output .
|
||||
|
||||
# Run tests
|
||||
test: generate lint test-ci
|
||||
test: generate fmt lint test-ci
|
||||
|
||||
# Run ci tests
|
||||
test-ci: mocks tidy
|
||||
go test ./... -coverprofile=coverage.out.tmp
|
||||
test-ci: mocks tidy tb.ginkgo
|
||||
$(TB_GINKGO) --cover --coverprofile coverage.out.tmp ./...
|
||||
cat coverage.out.tmp | grep -v "_generated.go" > coverage.out
|
||||
go tool cover -func=coverage.out
|
||||
|
||||
mocks: mockgen
|
||||
$(MOCKGEN) -package client -destination pkg/mocks/client/mock.go github.com/bakito/adguardhome-sync/pkg/client Client
|
||||
$(MOCKGEN) -package client -destination pkg/mocks/flags/mock.go github.com/bakito/adguardhome-sync/pkg/config Flags
|
||||
mocks: tb.mockgen
|
||||
$(TB_MOCKGEN) -package client -destination pkg/mocks/client/mock.go github.com/bakito/adguardhome-sync/pkg/client Client
|
||||
$(TB_MOCKGEN) -package client -destination pkg/mocks/flags/mock.go github.com/bakito/adguardhome-sync/pkg/config Flags
|
||||
|
||||
release: semver goreleaser
|
||||
@version=$$($(LOCALBIN)/semver); \
|
||||
release: tb.semver tb.goreleaser
|
||||
@version=$$($(TB_SEMVER)); \
|
||||
git tag -s $$version -m"Release $$version"
|
||||
$(GORELEASER) --clean
|
||||
$(TB_GORELEASER) --clean
|
||||
|
||||
test-release: goreleaser
|
||||
$(GORELEASER) --skip-publish --snapshot --clean
|
||||
|
||||
## toolbox - start
|
||||
## Current working directory
|
||||
LOCALDIR ?= $(shell which cygpath > /dev/null 2>&1 && cygpath -m $$(pwd) || pwd)
|
||||
## Location to install dependencies to
|
||||
LOCALBIN ?= $(LOCALDIR)/bin
|
||||
$(LOCALBIN):
|
||||
mkdir -p $(LOCALBIN)
|
||||
|
||||
## Tool Binaries
|
||||
SEMVER ?= $(LOCALBIN)/semver
|
||||
OAPI_CODEGEN ?= $(LOCALBIN)/oapi-codegen
|
||||
MOCKGEN ?= $(LOCALBIN)/mockgen
|
||||
GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint
|
||||
GORELEASER ?= $(LOCALBIN)/goreleaser
|
||||
DEEPCOPY_GEN ?= $(LOCALBIN)/deepcopy-gen
|
||||
|
||||
## Tool Versions
|
||||
SEMVER_VERSION ?= v1.1.3
|
||||
OAPI_CODEGEN_VERSION ?= v2.0.0
|
||||
MOCKGEN_VERSION ?= v1.6.0
|
||||
GOLANGCI_LINT_VERSION ?= v1.55.2
|
||||
GORELEASER_VERSION ?= v1.23.0
|
||||
DEEPCOPY_GEN_VERSION ?= v0.29.0
|
||||
|
||||
## Tool Installer
|
||||
.PHONY: semver
|
||||
semver: $(SEMVER) ## Download semver locally if necessary.
|
||||
$(SEMVER): $(LOCALBIN)
|
||||
test -s $(LOCALBIN)/semver || GOBIN=$(LOCALBIN) go install github.com/bakito/semver@$(SEMVER_VERSION)
|
||||
.PHONY: oapi-codegen
|
||||
oapi-codegen: $(OAPI_CODEGEN) ## Download oapi-codegen locally if necessary.
|
||||
$(OAPI_CODEGEN): $(LOCALBIN)
|
||||
test -s $(LOCALBIN)/oapi-codegen || GOBIN=$(LOCALBIN) go install github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@$(OAPI_CODEGEN_VERSION)
|
||||
.PHONY: mockgen
|
||||
mockgen: $(MOCKGEN) ## Download mockgen locally if necessary.
|
||||
$(MOCKGEN): $(LOCALBIN)
|
||||
test -s $(LOCALBIN)/mockgen || GOBIN=$(LOCALBIN) go install github.com/golang/mock/mockgen@$(MOCKGEN_VERSION)
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
|
||||
$(GOLANGCI_LINT): $(LOCALBIN)
|
||||
test -s $(LOCALBIN)/golangci-lint || GOBIN=$(LOCALBIN) go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)
|
||||
.PHONY: goreleaser
|
||||
goreleaser: $(GORELEASER) ## Download goreleaser locally if necessary.
|
||||
$(GORELEASER): $(LOCALBIN)
|
||||
test -s $(LOCALBIN)/goreleaser || GOBIN=$(LOCALBIN) go install github.com/goreleaser/goreleaser@$(GORELEASER_VERSION)
|
||||
.PHONY: deepcopy-gen
|
||||
deepcopy-gen: $(DEEPCOPY_GEN) ## Download deepcopy-gen locally if necessary.
|
||||
$(DEEPCOPY_GEN): $(LOCALBIN)
|
||||
test -s $(LOCALBIN)/deepcopy-gen || GOBIN=$(LOCALBIN) go install k8s.io/code-generator/cmd/deepcopy-gen@$(DEEPCOPY_GEN_VERSION)
|
||||
|
||||
## Update Tools
|
||||
.PHONY: update-toolbox-tools
|
||||
update-toolbox-tools:
|
||||
@rm -f \
|
||||
$(LOCALBIN)/semver \
|
||||
$(LOCALBIN)/oapi-codegen \
|
||||
$(LOCALBIN)/mockgen \
|
||||
$(LOCALBIN)/golangci-lint \
|
||||
$(LOCALBIN)/goreleaser \
|
||||
$(LOCALBIN)/deepcopy-gen
|
||||
toolbox makefile -f $(LOCALDIR)/Makefile \
|
||||
github.com/bakito/semver \
|
||||
github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen \
|
||||
github.com/golang/mock/mockgen \
|
||||
github.com/golangci/golangci-lint/cmd/golangci-lint \
|
||||
github.com/goreleaser/goreleaser \
|
||||
k8s.io/code-generator/cmd/deepcopy-gen@github.com/kubernetes/code-generator
|
||||
## toolbox - end
|
||||
test-release: tb.goreleaser
|
||||
$(TB_GORELEASER) --skip=publish --snapshot --clean
|
||||
|
||||
start-replica:
|
||||
docker run --pull always --name adguardhome-replica -p 9091:3000 --rm adguard/adguardhome:latest
|
||||
@@ -130,12 +67,15 @@ kind-create:
|
||||
kind-test:
|
||||
@./testdata/e2e/bin/install-chart.sh
|
||||
|
||||
model: oapi-codegen
|
||||
# renovate: packageName=AdguardTeam/AdGuardHome
|
||||
ADGUARD_HOME_VERSION ?= v0.107.57
|
||||
|
||||
model: tb.oapi-codegen
|
||||
@mkdir -p tmp
|
||||
go run openapi/main.go v0.107.43
|
||||
$(OAPI_CODEGEN) -package model -generate types,client -config .oapi-codegen.yaml tmp/schema.yaml > pkg/client/model/model_generated.go
|
||||
go run openapi/main.go $(ADGUARD_HOME_VERSION)
|
||||
$(TB_OAPI_CODEGEN) -package model -generate types,client -config .oapi-codegen.yaml tmp/schema.yaml > pkg/client/model/model_generated.go
|
||||
|
||||
model-diff:
|
||||
go run openapi/main.go v0.107.43
|
||||
go run openapi/main.go $(ADGUARD_HOME_VERSION)
|
||||
go run openapi/main.go
|
||||
diff tmp/schema.yaml tmp/schema-master.yaml
|
||||
|
||||
184
README.md
184
README.md
@@ -1,4 +1,5 @@
|
||||
[](https://github.com/bakito/adguardhome-sync/actions/workflows/go.yml)
|
||||
[](https://github.com/bakito/adguardhome-sync/actions/workflows/e2e.yaml)
|
||||
[](https://goreportcard.com/report/github.com/bakito/adguardhome-sync)
|
||||
[](https://coveralls.io/github/bakito/adguardhome-sync?branch=main)
|
||||
|
||||
@@ -6,38 +7,11 @@
|
||||
|
||||
Synchronize [AdGuardHome](https://github.com/AdguardTeam/AdGuardHome) config to replica instances.
|
||||
|
||||
## FAQ
|
||||
## FAQ & Deprecations
|
||||
|
||||
Please check the wiki for [FAQ](https://github.com/bakito/adguardhome-sync/wiki/FAQ).
|
||||
|
||||
## Deprecation of Environment Variables as of v0.6.0
|
||||
|
||||
The following environment variables have been deprecated and replaced with better readable ones:
|
||||
Deprecated variables are still supported but will be logged as warning.
|
||||
| Deprecated | Replacement |
|
||||
| :----------- |:----------- |
|
||||
| RUNONSTART | RUN_ON_START |
|
||||
| PRINTCONFIGONLY | PRINT_CONFIG_ONLY |
|
||||
| CONTINUE_ON_ERROR | CONTINUE_ON_ERROR |
|
||||
| API_DARKMODE | API_DARK_MODE |
|
||||
| FEATURES_DHCP_SERVERCONFIG | FEATURES_DHCP_SERVER_CONFIG |
|
||||
| FEATURES_DHCP_STATICLEASES | FEATURES_DHCP_STATIC_LEASES |
|
||||
| FEATURES_DNS_ACCESSLISTS | FEATURES_DNS_ACCESS_LISTS |
|
||||
| FEATURES_DNS_SERVERCONFIG | FEATURES_DNS_SERVER_CONFIG |
|
||||
| FEATURES_GENERALSETTINGS | FEATURES_GENERAL_SETTINGS |
|
||||
| FEATURES_QUERYLOGCONFIG | FEATURES_QUERY_LOG_CONFIG |
|
||||
| FEATURES_STATSCONFIG | FEATURES_STATS_CONFIG |
|
||||
| FEATURES_CLIENTSETTINGS | FEATURES_CLIENT_SETTINGS |
|
||||
| ORIGIN_WEBURL | ORIGIN_WEB_URL |
|
||||
| ORIGIN_APIPATH | ORIGIN_API_PATH |
|
||||
| ORIGIN_INSECURE_SKIP_VERIFY | ORIGIN_INSECURE_SKIP_VERIFY |
|
||||
| *REPLICA_WEBURL | REPLICA_WEB_URL |
|
||||
| *REPLICA_APIPATH | REPLICA_API_PATH |
|
||||
| *REPLICA_INSECURE_SKIP_VERIFY | REPLICA_INSECURE_SKIP_VERIFY |
|
||||
| *REPLICA_AUTOSETUP | REPLICA_AUTO_SETUP |
|
||||
| *REPLICA_INTERFACENAME | REPLICA_INTERFACE_NAME |
|
||||
|
||||
\* is also valid for numbered `REPLICA#_` variables
|
||||
Please check the wiki
|
||||
for [FAQ](https://github.com/bakito/adguardhome-sync/wiki/FAQ)
|
||||
and [Deprecations](https://github.com/bakito/adguardhome-sync/wiki/Deprecations).
|
||||
|
||||
## Current sync features
|
||||
|
||||
@@ -48,6 +22,7 @@ Deprecated variables are still supported but will be logged as warning.
|
||||
- Clients
|
||||
- DNS Config
|
||||
- DHCP Config
|
||||
- Theme
|
||||
|
||||
By default, all features are enabled. Single features can be disabled in the config.
|
||||
|
||||
@@ -88,18 +63,63 @@ export ORIGIN_URL=https://192.168.1.2:3000
|
||||
export ORIGIN_USERNAME=username
|
||||
export ORIGIN_PASSWORD=password
|
||||
# export ORIGIN_COOKIE=Origin-Cookie-Name=CCCOOOKKKIIIEEE
|
||||
export REPLICA_URL=http://192.168.1.3
|
||||
export REPLICA_USERNAME=username
|
||||
export REPLICA_PASSWORD=password
|
||||
export REPLICA1_URL=http://192.168.1.3
|
||||
export REPLICA1_USERNAME=username
|
||||
export REPLICA1_PASSWORD=password
|
||||
# export REPLICA_COOKIE=Replica-Cookie-Name=CCCOOOKKKIIIEEE
|
||||
|
||||
# run once
|
||||
adguardhome-sync run
|
||||
|
||||
# run as daemon
|
||||
adguardhome-sync run --cron "*/10 * * * *"
|
||||
adguardhome-sync run --cron "0 */2 * * *"
|
||||
```
|
||||
|
||||
### Run as Linux Service via Systemd
|
||||
|
||||
> Verified on Ubuntu Linux 24.04
|
||||
|
||||
Assume you have downloaded the the `adguardhome-sync` binary to `/opt/adguardhome-sync`.
|
||||
|
||||
Create systemd service file `/opt/adguardhome-sync/adguardhome-sync.service`:
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description = AdGuardHome Sync
|
||||
After = network.target
|
||||
|
||||
[Service]
|
||||
ExecStart = /opt/adguardhome-sync/adguardhome-sync --config /opt/adguardhome-sync/adguardhome-sync.yaml run
|
||||
|
||||
[Install]
|
||||
WantedBy = multi-user.target
|
||||
|
||||
```
|
||||
|
||||
Create a configuration file `/opt/adguardhome-sync/adguardhome-sync.yaml`, please follow [Config file](#config-file-1)
|
||||
section below for details.
|
||||
|
||||
Install and enable service:
|
||||
|
||||
```bash
|
||||
sudo cp /opt/adguardhome-sync/adguardhome-sync.service /etc/systemd/system/
|
||||
|
||||
sudo systemctl enable adguardhome-sync.service
|
||||
|
||||
sudo systemctl start adguardhome-sync.service
|
||||
|
||||
```
|
||||
|
||||
Then you can check the status:
|
||||
|
||||
```bash
|
||||
sudo systemctl status adguardhome-sync.service
|
||||
|
||||
```
|
||||
|
||||
If web UI has been enabled in configuration (default port is 8080), can also check the status via
|
||||
`http://<server-IP>:8080`
|
||||
|
||||
## Run Windows
|
||||
|
||||
```bash
|
||||
@@ -116,10 +136,10 @@ set ORIGIN_USERNAME=username
|
||||
set ORIGIN_PASSWORD=password
|
||||
# set ORIGIN_COOKIE=Origin-Cookie-Name=CCCOOOKKKIIIEEE
|
||||
|
||||
set REPLICA_URL=http://192.168.2.2:3000
|
||||
set REPLICA_USERNAME=username
|
||||
set REPLICA_PASSWORD=password
|
||||
# set REPLICA_COOKIE=Replica-Cookie-Name=CCCOOOKKKIIIEEE
|
||||
set REPLICA1_URL=http://192.168.2.2:3000
|
||||
set REPLICA1_USERNAME=username
|
||||
set REPLICA1_PASSWORD=password
|
||||
# set REPLICA1_COOKIE=Replica-Cookie-Name=CCCOOOKKKIIIEEE
|
||||
|
||||
set FEATURES_DHCP=false
|
||||
set FEATURES_DHCP_SERVERCONFIG=false
|
||||
@@ -129,7 +149,7 @@ set FEATURES_DHCP_STATICLEASES=false
|
||||
adguardhome-sync run
|
||||
|
||||
# run as daemon
|
||||
adguardhome-sync run --cron "*/10 * * * *"
|
||||
adguardhome-sync run --cron "0 */2 * * *"
|
||||
```
|
||||
|
||||
## docker cli
|
||||
@@ -179,36 +199,47 @@ services:
|
||||
|
||||
ORIGIN_USERNAME: "username"
|
||||
ORIGIN_PASSWORD: "password"
|
||||
REPLICA_URL: "http://192.168.1.3"
|
||||
REPLICA_USERNAME: "username"
|
||||
REPLICA_PASSWORD: "password"
|
||||
REPLICA1_URL: "http://192.168.1.4"
|
||||
REPLICA1_URL: "http://192.168.1.3"
|
||||
REPLICA1_USERNAME: "username"
|
||||
REPLICA1_PASSWORD: "password"
|
||||
REPLICA1_API_PATH: "/some/path/control"
|
||||
# REPLICA1_WEB_URL: "https://some-other.url" # used in the web interface (default: <replica-url>
|
||||
# REPLICA1_AUTO_SETUP: true # if true, AdGuardHome is automatically initialized.
|
||||
# REPLICA1_INTERFACE_NAME: 'ens18' # use custom dhcp interface name
|
||||
# REPLICA1_DHCP_SERVER_ENABLED: true/false (optional) enables/disables the dhcp server on the replica
|
||||
CRON: "*/10 * * * *" # run every 10 minutes
|
||||
RUNONSTART: true
|
||||
REPLICA2_URL: "http://192.168.1.4"
|
||||
REPLICA2_USERNAME: "username"
|
||||
REPLICA2_PASSWORD: "password"
|
||||
REPLICA2_API_PATH: "/some/path/control"
|
||||
# REPLICA2_WEB_URL: "https://some-other.url" # used in the web interface (default: <replica-url>
|
||||
# REPLICA2_AUTO_SETUP: true # if true, AdGuardHome is automatically initialized.
|
||||
# REPLICA2_INTERFACE_NAME: 'ens18' # use custom dhcp interface name
|
||||
# REPLICA2_DHCP_SERVER_ENABLED: true/false (optional) enables/disables the dhcp server on the replica
|
||||
CRON: "0 */2 * * *" # run every 2 hours
|
||||
RUN_ON_START: "true"
|
||||
# CONTINUE_ON_ERROR: false # If enabled, the synchronisation task will not fail on single errors, but will log the errors and continue
|
||||
|
||||
# Configure the sync API server, disabled if api port is 0
|
||||
API_PORT: 8080
|
||||
# API_DARK_MODE: "true"
|
||||
# API_USERNAME: admin
|
||||
# API_PASSWORD: secret
|
||||
# the directory of the provided tls certs
|
||||
# API_TLS_CERT_DIR: /path/to/certs
|
||||
# the name of the cert file (default: tls.crt)
|
||||
# API_TLS_CERT_NAME: foo.crt
|
||||
# the name of the key file (default: tls.key)
|
||||
# API_TLS_KEY_NAME: bar.key
|
||||
# API_METRICS_ENABLED: "true"
|
||||
|
||||
# Configure sync features; by default all features are enabled.
|
||||
# FEATURES_GENERAL_SETTINGS: true
|
||||
# FEATURES_QUERY_LOG_CONFIG: true
|
||||
# FEATURES_STATS_CONFIG: true
|
||||
# FEATURES_CLIENT_SETTINGS: true
|
||||
# FEATURES_SERVICES: true
|
||||
# FEATURES_FILTERS: true
|
||||
# FEATURES_DHCP_SERVER_CONFIG: true
|
||||
# FEATURES_DHCP_STATIC_LEASES: true
|
||||
# FEATURES_DNS_SERVER_CONFIG: true
|
||||
# FEATURES_DNS_ACCESS_LISTS: true
|
||||
# FEATURES_DNS_REWRITES: true
|
||||
# FEATURES_GENERAL_SETTINGS: "true"
|
||||
# FEATURES_QUERY_LOG_CONFIG: "true"
|
||||
# FEATURES_STATS_CONFIG: "true"
|
||||
# FEATURES_CLIENT_SETTINGS: "true"
|
||||
# FEATURES_SERVICES: "true"
|
||||
# FEATURES_FILTERS: "true"
|
||||
# FEATURES_DHCP_SERVER_CONFIG: "true"
|
||||
# FEATURES_DHCP_STATIC_LEASES: "true"
|
||||
# FEATURES_DNS_SERVER_CONFIG: "true"
|
||||
# FEATURES_DNS_ACCESS_LISTS: "true"
|
||||
# FEATURES_DNS_REWRITES: "true"
|
||||
# FEATURES_THEME: "true" # if false the UI theme is not synced
|
||||
ports:
|
||||
- 8080:8080
|
||||
restart: unless-stopped
|
||||
@@ -220,7 +251,7 @@ location: $HOME/.adguardhome-sync.yaml
|
||||
|
||||
```yaml
|
||||
# cron expression to run in daemon mode. (default; "" = runs only once)
|
||||
cron: "*/10 * * * *"
|
||||
cron: "0 */2 * * *"
|
||||
|
||||
# runs the synchronisation on startup
|
||||
runOnStart: true
|
||||
@@ -261,6 +292,21 @@ api:
|
||||
# enable api dark mode
|
||||
darkMode: true
|
||||
|
||||
# enable metrics on path '/metrics' (api port must be != 0)
|
||||
# metrics:
|
||||
# enabled: true
|
||||
# scrapeInterval: 30s
|
||||
# queryLogLimit: 10000
|
||||
|
||||
# enable tls for the api server
|
||||
# tls:
|
||||
# # the directory of the provided tls certs
|
||||
# certDir: /path/to/certs
|
||||
# # the name of the cert file (default: tls.crt)
|
||||
# certName: foo.crt
|
||||
# # the name of the key file (default: tls.key)
|
||||
# keyName: bar.key
|
||||
|
||||
# Configure sync features; by default all features are enabled.
|
||||
features:
|
||||
generalSettings: true
|
||||
@@ -280,7 +326,7 @@ features:
|
||||
|
||||
## Log Level
|
||||
|
||||
The log level can be set with the environment variable: LOG_LEVEL
|
||||
The log level can be set with the environment variable: `LOG_LEVEL`
|
||||
|
||||
The following log levels are supported (default: info)
|
||||
|
||||
@@ -288,3 +334,13 @@ The following log levels are supported (default: info)
|
||||
- info
|
||||
- warn
|
||||
- error
|
||||
|
||||
## Log Format
|
||||
|
||||
Default log format is `console`.
|
||||
It can be changed to `json` by setting the environment variable: `LOG_FORMAT=json`.
|
||||
|
||||
## Video Tutorials
|
||||
|
||||
- [Como replicar la configuración de tu servidor DNS Adguard automáticamente - Tu servidor Part #12](https://www.youtube.com/watch?v=1LPeu_JG064) (
|
||||
Spanish) by [Jonatan Castro](https://github.com/jcastro)
|
||||
|
||||
15
cmd/run.go
15
cmd/run.go
@@ -50,7 +50,8 @@ func init() {
|
||||
doCmd.PersistentFlags().Bool(config.FlagContinueOnError, false, "If enabled, the synchronisation task "+
|
||||
"will not fail on single errors, but will log the errors and continue.")
|
||||
|
||||
doCmd.PersistentFlags().Int(config.FlagApiPort, 8080, "Sync API Port, the API endpoint will be started to enable remote triggering; if 0 port API is disabled.")
|
||||
doCmd.PersistentFlags().
|
||||
Int(config.FlagApiPort, 8080, "Sync API Port, the API endpoint will be started to enable remote triggering; if 0 port API is disabled.")
|
||||
doCmd.PersistentFlags().String(config.FlagApiUsername, "", "Sync API username")
|
||||
doCmd.PersistentFlags().String(config.FlagApiPassword, "", "Sync API password")
|
||||
doCmd.PersistentFlags().String(config.FlagApiDarkMode, "", "API UI in dark mode")
|
||||
@@ -70,7 +71,8 @@ func init() {
|
||||
doCmd.PersistentFlags().Bool(config.FlagFeatureFilters, true, "Enable filters sync feature")
|
||||
|
||||
doCmd.PersistentFlags().String(config.FlagOriginURL, "", "Origin instance url")
|
||||
doCmd.PersistentFlags().String(config.FlagOriginWebURL, "", "Origin instance web url used in the web interface (default: <origin-url>)")
|
||||
doCmd.PersistentFlags().
|
||||
String(config.FlagOriginWebURL, "", "Origin instance web url used in the web interface (default: <origin-url>)")
|
||||
doCmd.PersistentFlags().String(config.FlagOriginApiPath, "/control", "Origin instance API path")
|
||||
doCmd.PersistentFlags().String(config.FlagOriginUsername, "", "Origin instance username")
|
||||
doCmd.PersistentFlags().String(config.FlagOriginPassword, "", "Origin instance password")
|
||||
@@ -78,12 +80,15 @@ func init() {
|
||||
doCmd.PersistentFlags().Bool(config.FlagOriginISV, false, "Enable Origin instance InsecureSkipVerify")
|
||||
|
||||
doCmd.PersistentFlags().String(config.FlagReplicaURL, "", "Replica instance url")
|
||||
doCmd.PersistentFlags().String(config.FlagReplicaWebURL, "", "Replica instance web url used in the web interface (default: <replica-url>)")
|
||||
doCmd.PersistentFlags().
|
||||
String(config.FlagReplicaWebURL, "", "Replica instance web url used in the web interface (default: <replica-url>)")
|
||||
doCmd.PersistentFlags().String(config.FlagReplicaApiPath, "/control", "Replica instance API path")
|
||||
doCmd.PersistentFlags().String(config.FlagReplicaUsername, "", "Replica instance username")
|
||||
doCmd.PersistentFlags().String(config.FlagReplicaPassword, "", "Replica instance password")
|
||||
doCmd.PersistentFlags().String(config.FlagReplicaCookie, "", "If Set, uses a cookie for authentication")
|
||||
doCmd.PersistentFlags().Bool(config.FlagReplicaISV, false, "Enable Replica instance InsecureSkipVerify")
|
||||
doCmd.PersistentFlags().Bool(config.FlagReplicaAutoSetup, false, "Enable automatic setup of new AdguardHome instances. This replaces the setup wizard.")
|
||||
doCmd.PersistentFlags().String(config.FlagReplicaInterfaceName, "", "Optional change the interface name of the replica if it differs from the master")
|
||||
doCmd.PersistentFlags().
|
||||
Bool(config.FlagReplicaAutoSetup, false, "Enable automatic setup of new AdguardHome instances. This replaces the setup wizard.")
|
||||
doCmd.PersistentFlags().
|
||||
String(config.FlagReplicaInterfaceName, "", "Optional change the interface name of the replica if it differs from the master")
|
||||
}
|
||||
|
||||
87
go.mod
87
go.mod
@@ -1,69 +1,78 @@
|
||||
module github.com/bakito/adguardhome-sync
|
||||
|
||||
go 1.21
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/caarlos0/env/v10 v10.0.0
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/go-resty/resty/v2 v2.9.1
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/google/uuid v1.5.0
|
||||
github.com/caarlos0/env/v11 v11.3.1
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/go-faker/faker/v4 v4.6.0
|
||||
github.com/go-resty/resty/v2 v2.16.5
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/oapi-codegen/runtime v1.1.1
|
||||
github.com/onsi/ginkgo/v2 v2.13.2
|
||||
github.com/onsi/gomega v1.30.0
|
||||
github.com/onsi/ginkgo/v2 v2.22.2
|
||||
github.com/onsi/gomega v1.36.2
|
||||
github.com/prometheus/client_golang v1.21.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/spf13/cobra v1.8.0
|
||||
go.uber.org/zap v1.26.0
|
||||
golang.org/x/mod v0.14.0
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
|
||||
github.com/spf13/cobra v1.9.1
|
||||
go.uber.org/mock v0.5.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/mod v0.23.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/apimachinery v0.29.0
|
||||
k8s.io/apimachinery v0.32.2
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/bytedance/sonic v1.10.2 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-logr/logr v1.3.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.7.0 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.16.0 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/tools v0.28.0 // indirect
|
||||
google.golang.org/protobuf v1.36.1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/klog/v2 v2.110.1 // indirect
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
247
go.sum
247
go.sum
@@ -1,67 +1,66 @@
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
|
||||
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||
github.com/caarlos0/env/v10 v10.0.0 h1:yIHUBZGsyqCnpTkbjk8asUlx6RFhhEs+h7TOBdgdzXA=
|
||||
github.com/caarlos0/env/v10 v10.0.0/go.mod h1:ZfulV76NvVPw3tm591U4SwL3Xx9ldzBP9aGxzeN7G18=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
||||
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
|
||||
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
|
||||
github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-faker/faker/v4 v4.6.0 h1:6aOPzNptRiDwD14HuAnEtlTa+D1IfFuEHO8+vEFwjTs=
|
||||
github.com/go-faker/faker/v4 v4.6.0/go.mod h1:ZmrHuVtTTm2Em9e0Du6CJ9CADaLEzGXW62z1YqFH0m0=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
|
||||
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-resty/resty/v2 v2.9.1 h1:PIgGx4VrHvag0juCJ4dDv3MiFRlDmP0vicBucwf+gLM=
|
||||
github.com/go-resty/resty/v2 v2.9.1/go.mod h1:4/GYJVjh9nhkhGR6AUNW3XhpDYNUr+Uvy9gV/VGZIy4=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
||||
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
@@ -71,16 +70,20 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -88,157 +91,133 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
|
||||
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
|
||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
|
||||
github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
||||
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
|
||||
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
|
||||
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o=
|
||||
k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis=
|
||||
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
|
||||
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ=
|
||||
k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
||||
@@ -20,7 +20,9 @@ func main() {
|
||||
}
|
||||
log.Printf("Patching schema version %s\n", version)
|
||||
|
||||
resp, err := http.Get(fmt.Sprintf("https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/%s/openapi/openapi.yaml", version))
|
||||
resp, err := http.Get(
|
||||
fmt.Sprintf("https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/%s/openapi/openapi.yaml", version),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
@@ -53,10 +53,8 @@ func New(config types.AdGuardInstance) (Client, error) {
|
||||
u.Path = path.Clean(u.Path)
|
||||
cl := resty.New().SetBaseURL(u.String()).SetDisableWarn(true)
|
||||
|
||||
if config.InsecureSkipVerify {
|
||||
// #nosec G402 has to be explicitly enabled
|
||||
cl.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
// #nosec G402 has to be explicitly enabled
|
||||
cl.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: config.InsecureSkipVerify})
|
||||
|
||||
cookieParts := strings.Split(config.Cookie, "=")
|
||||
if len(cookieParts) == 2 {
|
||||
@@ -90,6 +88,8 @@ func New(config types.AdGuardInstance) (Client, error) {
|
||||
type Client interface {
|
||||
Host() string
|
||||
Status() (*model.ServerStatus, error)
|
||||
Stats() (*model.Stats, error)
|
||||
QueryLog(limit int) (*model.QueryLog, error)
|
||||
ToggleProtection(enable bool) error
|
||||
RewriteList() (*model.RewriteEntries, error)
|
||||
AddRewriteEntries(e ...model.RewriteEntry) error
|
||||
@@ -109,18 +109,16 @@ type Client interface {
|
||||
SetSafeSearchConfig(settings *model.SafeSearchConfig) error
|
||||
ProfileInfo() (*model.ProfileInfo, error)
|
||||
SetProfileInfo(settings *model.ProfileInfo) error
|
||||
BlockedServices() (*model.BlockedServicesArray, error)
|
||||
BlockedServicesSchedule() (*model.BlockedServicesSchedule, error)
|
||||
SetBlockedServices(services *model.BlockedServicesArray) error
|
||||
SetBlockedServicesSchedule(schedule *model.BlockedServicesSchedule) error
|
||||
Clients() (*model.Clients, error)
|
||||
AddClient(client *model.Client) error
|
||||
UpdateClient(client *model.Client) error
|
||||
DeleteClient(client *model.Client) error
|
||||
QueryLogConfig() (*model.QueryLogConfig, error)
|
||||
SetQueryLogConfig(*model.QueryLogConfig) error
|
||||
StatsConfig() (*model.StatsConfig, error)
|
||||
SetStatsConfig(sc *model.StatsConfig) error
|
||||
QueryLogConfig() (*model.QueryLogConfigWithIgnored, error)
|
||||
SetQueryLogConfig(*model.QueryLogConfigWithIgnored) error
|
||||
StatsConfig() (*model.GetStatsConfigResponse, error)
|
||||
SetStatsConfig(sc *model.PutStatsConfigUpdateRequest) error
|
||||
Setup() error
|
||||
AccessList() (*model.AccessList, error)
|
||||
SetAccessList(*model.AccessList) error
|
||||
@@ -160,6 +158,21 @@ func (cl *client) Status() (*model.ServerStatus, error) {
|
||||
return status, err
|
||||
}
|
||||
|
||||
func (cl *client) Stats() (*model.Stats, error) {
|
||||
stats := &model.Stats{}
|
||||
err := cl.doGet(cl.client.R().EnableTrace().SetResult(stats), "stats")
|
||||
return stats, err
|
||||
}
|
||||
|
||||
func (cl *client) QueryLog(limit int) (*model.QueryLog, error) {
|
||||
ql := &model.QueryLog{}
|
||||
err := cl.doGet(
|
||||
cl.client.R().EnableTrace().SetResult(ql),
|
||||
fmt.Sprintf(`querylog?limit=%d&response_status="all"`, limit),
|
||||
)
|
||||
return ql, err
|
||||
}
|
||||
|
||||
func (cl *client) RewriteList() (*model.RewriteEntries, error) {
|
||||
rewrites := &model.RewriteEntries{}
|
||||
err := cl.doGet(cl.client.R().EnableTrace().SetResult(&rewrites), "/rewrite/list")
|
||||
@@ -167,8 +180,7 @@ func (cl *client) RewriteList() (*model.RewriteEntries, error) {
|
||||
}
|
||||
|
||||
func (cl *client) AddRewriteEntries(entries ...model.RewriteEntry) error {
|
||||
for i := range entries {
|
||||
e := entries[i]
|
||||
for _, e := range entries {
|
||||
cl.log.With("domain", e.Domain, "answer", e.Answer).Info("Add DNS rewrite entry")
|
||||
err := cl.doPost(cl.client.R().EnableTrace().SetBody(&e), "/rewrite/add")
|
||||
if err != nil {
|
||||
@@ -179,8 +191,7 @@ func (cl *client) AddRewriteEntries(entries ...model.RewriteEntry) error {
|
||||
}
|
||||
|
||||
func (cl *client) DeleteRewriteEntries(entries ...model.RewriteEntry) error {
|
||||
for i := range entries {
|
||||
e := entries[i]
|
||||
for _, e := range entries {
|
||||
cl.log.With("domain", e.Domain, "answer", e.Answer).Info("Delete DNS rewrite entry")
|
||||
err := cl.doPost(cl.client.R().EnableTrace().SetBody(&e), "/rewrite/delete")
|
||||
if err != nil {
|
||||
@@ -252,7 +263,10 @@ func (cl *client) UpdateFilter(whitelist bool, f model.Filter) error {
|
||||
|
||||
func (cl *client) RefreshFilters(whitelist bool) error {
|
||||
cl.log.With("whitelist", whitelist).Info("Refresh filter")
|
||||
return cl.doPost(cl.client.R().EnableTrace().SetBody(&model.FilterRefreshRequest{Whitelist: utils.Ptr(whitelist)}), "/filtering/refresh")
|
||||
return cl.doPost(
|
||||
cl.client.R().EnableTrace().SetBody(&model.FilterRefreshRequest{Whitelist: utils.Ptr(whitelist)}),
|
||||
"/filtering/refresh",
|
||||
)
|
||||
}
|
||||
|
||||
func (cl *client) ToggleProtection(enable bool) error {
|
||||
@@ -261,7 +275,11 @@ func (cl *client) ToggleProtection(enable bool) error {
|
||||
}
|
||||
|
||||
func (cl *client) SetCustomRules(rules *[]string) error {
|
||||
cl.log.With("rules", len(*rules)).Info("Set user rules")
|
||||
var l int
|
||||
if rules != nil {
|
||||
l = len(*rules)
|
||||
}
|
||||
cl.log.With("rules", l).Info("Set user rules")
|
||||
return cl.doPost(cl.client.R().EnableTrace().SetBody(&model.SetRulesRequest{Rules: rules}), "/filtering/set_rules")
|
||||
}
|
||||
|
||||
@@ -273,17 +291,6 @@ func (cl *client) ToggleFiltering(enabled bool, interval int) error {
|
||||
}), "/filtering/config")
|
||||
}
|
||||
|
||||
func (cl *client) BlockedServices() (*model.BlockedServicesArray, error) {
|
||||
svcs := &model.BlockedServicesArray{}
|
||||
err := cl.doGet(cl.client.R().EnableTrace().SetResult(svcs), "/blocked_services/list")
|
||||
return svcs, err
|
||||
}
|
||||
|
||||
func (cl *client) SetBlockedServices(services *model.BlockedServicesArray) error {
|
||||
cl.log.With("services", model.ArrayString(services)).Info("Set blocked services")
|
||||
return cl.doPost(cl.client.R().EnableTrace().SetBody(services), "/blocked_services/set")
|
||||
}
|
||||
|
||||
func (cl *client) BlockedServicesSchedule() (*model.BlockedServicesSchedule, error) {
|
||||
sched := &model.BlockedServicesSchedule{}
|
||||
err := cl.doGet(cl.client.R().EnableTrace().SetResult(sched), "/blocked_services/get")
|
||||
@@ -308,7 +315,10 @@ func (cl *client) AddClient(client *model.Client) error {
|
||||
|
||||
func (cl *client) UpdateClient(client *model.Client) error {
|
||||
cl.log.With("name", *client.Name).Info("Update client settings")
|
||||
return cl.doPost(cl.client.R().EnableTrace().SetBody(&model.ClientUpdate{Name: client.Name, Data: client}), "/clients/update")
|
||||
return cl.doPost(
|
||||
cl.client.R().EnableTrace().SetBody(&model.ClientUpdate{Name: client.Name, Data: client}),
|
||||
"/clients/update",
|
||||
)
|
||||
}
|
||||
|
||||
func (cl *client) DeleteClient(client *model.Client) error {
|
||||
@@ -316,26 +326,27 @@ func (cl *client) DeleteClient(client *model.Client) error {
|
||||
return cl.doPost(cl.client.R().EnableTrace().SetBody(client), "/clients/delete")
|
||||
}
|
||||
|
||||
func (cl *client) QueryLogConfig() (*model.QueryLogConfig, error) {
|
||||
qlc := &model.QueryLogConfig{}
|
||||
err := cl.doGet(cl.client.R().EnableTrace().SetResult(qlc), "/querylog_info")
|
||||
func (cl *client) QueryLogConfig() (*model.QueryLogConfigWithIgnored, error) {
|
||||
qlc := &model.QueryLogConfigWithIgnored{}
|
||||
err := cl.doGet(cl.client.R().EnableTrace().SetResult(qlc), "/querylog/config")
|
||||
return qlc, err
|
||||
}
|
||||
|
||||
func (cl *client) SetQueryLogConfig(qlc *model.QueryLogConfig) error {
|
||||
cl.log.With("enabled", *qlc.Enabled, "interval", *qlc.Interval, "anonymizeClientIP", *qlc.AnonymizeClientIp).Info("Set query log config")
|
||||
return cl.doPost(cl.client.R().EnableTrace().SetBody(qlc), "/querylog_config")
|
||||
func (cl *client) SetQueryLogConfig(qlc *model.QueryLogConfigWithIgnored) error {
|
||||
cl.log.With("enabled", *qlc.Enabled, "interval", *qlc.Interval, "anonymizeClientIP", *qlc.AnonymizeClientIp).
|
||||
Info("Set query log config")
|
||||
return cl.doPut(cl.client.R().EnableTrace().SetBody(qlc), "/querylog/config/update")
|
||||
}
|
||||
|
||||
func (cl *client) StatsConfig() (*model.StatsConfig, error) {
|
||||
stats := &model.StatsConfig{}
|
||||
err := cl.doGet(cl.client.R().EnableTrace().SetResult(stats), "/stats_info")
|
||||
func (cl *client) StatsConfig() (*model.GetStatsConfigResponse, error) {
|
||||
stats := &model.GetStatsConfigResponse{}
|
||||
err := cl.doGet(cl.client.R().EnableTrace().SetResult(stats), "/stats/config")
|
||||
return stats, err
|
||||
}
|
||||
|
||||
func (cl *client) SetStatsConfig(sc *model.StatsConfig) error {
|
||||
cl.log.With("interval", *sc.Interval).Info("Set stats config")
|
||||
return cl.doPost(cl.client.R().EnableTrace().SetBody(sc), "/stats_config")
|
||||
func (cl *client) SetStatsConfig(sc *model.PutStatsConfigUpdateRequest) error {
|
||||
cl.log.With("interval", sc.Interval).Info("Set stats config")
|
||||
return cl.doPut(cl.client.R().EnableTrace().SetBody(sc), "/stats/config/update")
|
||||
}
|
||||
|
||||
func (cl *client) Setup() error {
|
||||
|
||||
@@ -97,6 +97,20 @@ var _ = Describe("Client", func() {
|
||||
err = cl.DeleteFilter(true, model.Filter{Url: "bar"})
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
It("should set empty filter rules", func() {
|
||||
ts, cl = ClientPost("/filtering/set_rules",
|
||||
`{"rules":[]}`,
|
||||
)
|
||||
err := cl.SetCustomRules(utils.Ptr([]string{}))
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
It("should set nil filter rules", func() {
|
||||
ts, cl = ClientPost("/filtering/set_rules",
|
||||
`{}`,
|
||||
)
|
||||
err := cl.SetCustomRules(nil)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("Status", func() {
|
||||
@@ -123,7 +137,14 @@ var _ = Describe("Client", func() {
|
||||
|
||||
Context("Setup", func() {
|
||||
It("should add setup the instance", func() {
|
||||
ts, cl = ClientPost("/install/configure", fmt.Sprintf(`{"web":{"ip":"0.0.0.0","port":3000,"status":"","can_autofix":false},"dns":{"ip":"0.0.0.0","port":53,"status":"","can_autofix":false},"username":"%s","password":"%s"}`, username, password))
|
||||
ts, cl = ClientPost(
|
||||
"/install/configure",
|
||||
fmt.Sprintf(
|
||||
`{"web":{"ip":"0.0.0.0","port":3000,"status":"","can_autofix":false},"dns":{"ip":"0.0.0.0","port":53,"status":"","can_autofix":false},"username":"%s","password":"%s"}`,
|
||||
username,
|
||||
password,
|
||||
),
|
||||
)
|
||||
err := cl.Setup()
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
@@ -225,20 +246,6 @@ var _ = Describe("Client", func() {
|
||||
})
|
||||
})
|
||||
|
||||
Context("BlockedServices", func() {
|
||||
It("should read BlockedServices", func() {
|
||||
ts, cl = ClientGet("blockedservices-list.json", "/blocked_services/list")
|
||||
s, err := cl.BlockedServices()
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(*s).Should(HaveLen(2))
|
||||
})
|
||||
It("should set BlockedServices", func() {
|
||||
ts, cl = ClientPost("/blocked_services/set", `["bar","foo"]`)
|
||||
err := cl.SetBlockedServices(&model.BlockedServicesArray{"foo", "bar"})
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("BlockedServicesSchedule", func() {
|
||||
It("should read BlockedServicesSchedule", func() {
|
||||
ts, cl = ClientGet("blockedservicesschedule-get.json", "/blocked_services/get")
|
||||
@@ -294,7 +301,7 @@ var _ = Describe("Client", func() {
|
||||
|
||||
Context("QueryLogConfig", func() {
|
||||
It("should read QueryLogConfig", func() {
|
||||
ts, cl = ClientGet("querylog_info.json", "/querylog_info")
|
||||
ts, cl = ClientGet("querylog_config.json", "/querylog/config")
|
||||
qlc, err := cl.QueryLogConfig()
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(qlc.Enabled).ShouldNot(BeNil())
|
||||
@@ -303,26 +310,36 @@ var _ = Describe("Client", func() {
|
||||
Ω(*qlc.Interval).Should(Equal(model.QueryLogConfigInterval(90)))
|
||||
})
|
||||
It("should set QueryLogConfig", func() {
|
||||
ts, cl = ClientPost("/querylog_config", `{"anonymize_client_ip":true,"enabled":true,"interval":123}`)
|
||||
ts, cl = ClientPut(
|
||||
"/querylog/config/update",
|
||||
`{"anonymize_client_ip":true,"enabled":true,"interval":123,"ignored":["foo.bar"]}`,
|
||||
)
|
||||
|
||||
var interval model.QueryLogConfigInterval = 123
|
||||
err := cl.SetQueryLogConfig(&model.QueryLogConfig{AnonymizeClientIp: utils.Ptr(true), Interval: &interval, Enabled: utils.Ptr(true)})
|
||||
err := cl.SetQueryLogConfig(&model.QueryLogConfigWithIgnored{
|
||||
QueryLogConfig: model.QueryLogConfig{
|
||||
AnonymizeClientIp: utils.Ptr(true),
|
||||
Interval: &interval,
|
||||
Enabled: utils.Ptr(true),
|
||||
},
|
||||
Ignored: []string{"foo.bar"},
|
||||
})
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
Context("StatsConfig", func() {
|
||||
It("should read StatsConfig", func() {
|
||||
ts, cl = ClientGet("stats_info.json", "/stats_info")
|
||||
ts, cl = ClientGet("stats_info.json", "/stats/config")
|
||||
sc, err := cl.StatsConfig()
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(sc.Interval).ShouldNot(BeNil())
|
||||
Ω(*sc.Interval).Should(Equal(model.StatsConfigInterval(1)))
|
||||
Ω(sc.Interval).Should(Equal(float32(1)))
|
||||
})
|
||||
It("should set StatsConfig", func() {
|
||||
ts, cl = ClientPost("/stats_config", `{"interval":123}`)
|
||||
ts, cl = ClientPost("/stats/config/update", `{"enabled":false,"ignored":null,"interval":123}`)
|
||||
|
||||
var interval model.StatsConfigInterval = 123
|
||||
err := cl.SetStatsConfig(&model.StatsConfig{Interval: &interval})
|
||||
var interval float32 = 123
|
||||
err := cl.SetStatsConfig(&model.PutStatsConfigUpdateRequest{Interval: interval})
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
@@ -347,8 +364,8 @@ var _ = Describe("Client", func() {
|
||||
|
||||
Context("doPost", func() {
|
||||
It("should return an error on status code != 200", func() {
|
||||
var interval model.StatsConfigInterval = 123
|
||||
err := cl.SetStatsConfig(&model.StatsConfig{Interval: &interval})
|
||||
var interval float32 = 123
|
||||
err := cl.SetStatsConfig(&model.PutStatsConfigUpdateRequest{Interval: interval})
|
||||
Ω(err).Should(HaveOccurred())
|
||||
Ω(err.Error()).Should(Equal("401 Unauthorized"))
|
||||
})
|
||||
|
||||
@@ -33,12 +33,11 @@ func New(config types.AdGuardInstance) (Client, error) {
|
||||
}
|
||||
u.Path = path.Clean(u.Path)
|
||||
|
||||
httpClient := &http.Client{}
|
||||
if config.InsecureSkipVerify {
|
||||
// #nosec G402 has to be explicitly enabled
|
||||
httpClient.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
httpClient := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
// #nosec G402 has to be explicitly enabled
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify},
|
||||
},
|
||||
}
|
||||
|
||||
aghClient, err := model.NewClient(u.String(), func(client *model.AdguardHomeClient) error {
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/utils"
|
||||
"github.com/jinzhu/copier"
|
||||
"go.uber.org/zap"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
// Clone the config
|
||||
@@ -166,11 +168,40 @@ func (cl *Client) Sort() {
|
||||
}
|
||||
}
|
||||
|
||||
// PrepareDiff timezone BlockedServicesSchedule might differ if all other fields are empty,
|
||||
// so we skip it in diff
|
||||
func (cl *Client) PrepareDiff() *string {
|
||||
var tz *string
|
||||
bss := cl.BlockedServicesSchedule
|
||||
if bss != nil && bss.Mon == nil && bss.Tue == nil && bss.Wed == nil &&
|
||||
bss.Thu == nil && bss.Fri == nil && bss.Sat == nil && bss.Sun == nil {
|
||||
|
||||
tz = cl.BlockedServicesSchedule.TimeZone
|
||||
cl.BlockedServicesSchedule.TimeZone = nil
|
||||
}
|
||||
return tz
|
||||
}
|
||||
|
||||
// AfterDiff reset after diff
|
||||
func (cl *Client) AfterDiff(tz *string) {
|
||||
if cl.BlockedServicesSchedule != nil {
|
||||
cl.BlockedServicesSchedule.TimeZone = tz
|
||||
}
|
||||
}
|
||||
|
||||
// Equals Clients equal check
|
||||
func (cl *Client) Equals(o *Client) bool {
|
||||
cl.Sort()
|
||||
o.Sort()
|
||||
|
||||
bssCl := cl.PrepareDiff()
|
||||
bssO := o.PrepareDiff()
|
||||
|
||||
defer func() {
|
||||
cl.AfterDiff(bssCl)
|
||||
o.AfterDiff(bssO)
|
||||
}()
|
||||
|
||||
return utils.JsonEquals(cl, o)
|
||||
}
|
||||
|
||||
@@ -189,8 +220,7 @@ func (clients *Clients) Merge(other *Clients) ([]*Client, []*Client, []*Client)
|
||||
current := make(map[string]*Client)
|
||||
if clients.Clients != nil {
|
||||
cc := *clients.Clients
|
||||
for i := range cc {
|
||||
client := cc[i]
|
||||
for _, client := range cc {
|
||||
current[*client.Name] = &client
|
||||
}
|
||||
}
|
||||
@@ -198,8 +228,7 @@ func (clients *Clients) Merge(other *Clients) ([]*Client, []*Client, []*Client)
|
||||
expected := make(map[string]*Client)
|
||||
if other.Clients != nil {
|
||||
oc := *other.Clients
|
||||
for i := range oc {
|
||||
client := oc[i]
|
||||
for _, client := range oc {
|
||||
expected[*client.Name] = &client
|
||||
}
|
||||
}
|
||||
@@ -292,15 +321,13 @@ func MergeFilters(this *[]Filter, other *[]Filter) ([]Filter, []Filter, []Filter
|
||||
var updates []Filter
|
||||
var removes []Filter
|
||||
if this != nil {
|
||||
for i := range *this {
|
||||
fi := (*this)[i]
|
||||
for _, fi := range *this {
|
||||
current[fi.Url] = &fi
|
||||
}
|
||||
}
|
||||
|
||||
if other != nil {
|
||||
for i := range *other {
|
||||
rr := (*other)[i]
|
||||
for _, rr := range *other {
|
||||
if c, ok := current[rr.Url]; ok {
|
||||
if !c.Equals(&rr) {
|
||||
updates = append(updates, rr)
|
||||
@@ -324,11 +351,16 @@ func (f *Filter) Equals(o *Filter) bool {
|
||||
return f.Enabled == o.Enabled && f.Url == o.Url && f.Name == o.Name
|
||||
}
|
||||
|
||||
type QueryLogConfigWithIgnored struct {
|
||||
QueryLogConfig
|
||||
|
||||
// Ignored List of host names, which should not be written to log
|
||||
Ignored []string `json:"ignored,omitempty"`
|
||||
}
|
||||
|
||||
// Equals QueryLogConfig equal check
|
||||
func (qlc *QueryLogConfig) Equals(o *QueryLogConfig) bool {
|
||||
return ptrEquals(qlc.Enabled, o.Enabled) &&
|
||||
ptrEquals(qlc.AnonymizeClientIp, o.AnonymizeClientIp) &&
|
||||
qlc.Interval.Equals(o.Interval)
|
||||
func (qlc *QueryLogConfigWithIgnored) Equals(o *QueryLogConfigWithIgnored) bool {
|
||||
return utils.JsonEquals(qlc, o)
|
||||
}
|
||||
|
||||
// Equals QueryLogConfigInterval equal check
|
||||
@@ -367,23 +399,22 @@ func (ssc *SafeSearchConfig) Equals(o *SafeSearchConfig) bool {
|
||||
ptrEquals(ssc.Youtube, o.Youtube)
|
||||
}
|
||||
|
||||
func (pi *ProfileInfo) Equals(o *ProfileInfo) bool {
|
||||
return pi.Language == o.Language &&
|
||||
pi.Theme == o.Theme
|
||||
func (pi *ProfileInfo) Equals(o *ProfileInfo, withTheme bool) bool {
|
||||
return pi.Language == o.Language && (!withTheme || pi.Theme == o.Theme)
|
||||
}
|
||||
|
||||
func (pi *ProfileInfo) ShouldSyncFor(o *ProfileInfo) *ProfileInfo {
|
||||
if pi.Equals(o) {
|
||||
func (pi *ProfileInfo) ShouldSyncFor(o *ProfileInfo, withTheme bool) *ProfileInfo {
|
||||
if pi.Equals(o, withTheme) {
|
||||
return nil
|
||||
}
|
||||
merged := &ProfileInfo{Name: pi.Name, Language: pi.Language, Theme: pi.Theme}
|
||||
if o.Language != "" {
|
||||
merged.Language = o.Language
|
||||
}
|
||||
if o.Theme != "" {
|
||||
if withTheme && o.Theme != "" {
|
||||
merged.Theme = o.Theme
|
||||
}
|
||||
if merged.Name == "" || merged.Language == "" || merged.Theme == "" || merged.Equals(pi) {
|
||||
if merged.Name == "" || merged.Language == "" || merged.Equals(pi, false) {
|
||||
return nil
|
||||
}
|
||||
return merged
|
||||
@@ -405,3 +436,70 @@ func ArrayString(a *[]string) string {
|
||||
sort.Strings(sorted)
|
||||
return fmt.Sprintf("[%s]", strings.Join(sorted, ","))
|
||||
}
|
||||
|
||||
func (c *DNSConfig) Sanitize(l *zap.SugaredLogger) {
|
||||
// disable UsePrivatePtrResolvers if not configured
|
||||
// https://github.com/AdguardTeam/AdGuardHome/issues/6820
|
||||
if c.UsePrivatePtrResolvers != nil && *c.UsePrivatePtrResolvers &&
|
||||
(c.LocalPtrUpstreams == nil || len(*c.LocalPtrUpstreams) == 0) {
|
||||
l.Warn(
|
||||
"disabling replica 'Use private reverse DNS resolvers' as no 'Private reverse DNS servers' are configured on origin",
|
||||
)
|
||||
c.UsePrivatePtrResolvers = utils.Ptr(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Equals GetStatsConfigResponse equal check
|
||||
func (sc *GetStatsConfigResponse) Equals(o *GetStatsConfigResponse) bool {
|
||||
return utils.JsonEquals(sc, o)
|
||||
}
|
||||
|
||||
func NewStats() *Stats {
|
||||
return &Stats{
|
||||
NumBlockedFiltering: ptr.To(0),
|
||||
NumReplacedParental: ptr.To(0),
|
||||
NumReplacedSafesearch: ptr.To(0),
|
||||
NumReplacedSafebrowsing: ptr.To(0),
|
||||
NumDnsQueries: ptr.To(0),
|
||||
|
||||
BlockedFiltering: ptr.To(make([]int, 24)),
|
||||
DnsQueries: ptr.To(make([]int, 24)),
|
||||
ReplacedParental: ptr.To(make([]int, 24)),
|
||||
ReplacedSafebrowsing: ptr.To(make([]int, 24)),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stats) Add(other *Stats) {
|
||||
s.NumBlockedFiltering = addInt(s.NumBlockedFiltering, other.NumBlockedFiltering)
|
||||
s.NumReplacedSafebrowsing = addInt(s.NumReplacedSafebrowsing, other.NumReplacedSafebrowsing)
|
||||
s.NumDnsQueries = addInt(s.NumDnsQueries, other.NumDnsQueries)
|
||||
s.NumReplacedSafesearch = addInt(s.NumReplacedSafesearch, other.NumReplacedSafesearch)
|
||||
s.NumReplacedParental = addInt(s.NumReplacedParental, other.NumReplacedParental)
|
||||
|
||||
s.BlockedFiltering = sumUp(s.BlockedFiltering, other.BlockedFiltering)
|
||||
s.DnsQueries = sumUp(s.DnsQueries, other.DnsQueries)
|
||||
s.ReplacedParental = sumUp(s.ReplacedParental, other.ReplacedParental)
|
||||
s.ReplacedSafebrowsing = sumUp(s.ReplacedSafebrowsing, other.ReplacedSafebrowsing)
|
||||
}
|
||||
|
||||
func addInt(t *int, add *int) *int {
|
||||
if add != nil {
|
||||
return ptr.To(*t + *add)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func sumUp(t *[]int, o *[]int) *[]int {
|
||||
if o != nil {
|
||||
tt := *t
|
||||
oo := *o
|
||||
var sum []int
|
||||
for i := 0; i < len(tt); i++ {
|
||||
if len(oo) >= i {
|
||||
sum = append(sum, tt[i]+oo[i])
|
||||
}
|
||||
}
|
||||
return &sum
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Package model provides primitives to interact with the openapi HTTP API.
|
||||
//
|
||||
// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0 DO NOT EDIT.
|
||||
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT.
|
||||
package model
|
||||
|
||||
import (
|
||||
@@ -37,6 +37,14 @@ const (
|
||||
Refused DNSConfigBlockingMode = "refused"
|
||||
)
|
||||
|
||||
// Defines values for DNSConfigUpstreamMode.
|
||||
const (
|
||||
MapConstDeprecatedTrueDescriptionUseloadBalanceInstead DNSConfigUpstreamMode = "map[const: deprecated:true description:Use `load_balance` instead.]"
|
||||
MapConstFastestAddr DNSConfigUpstreamMode = "map[const:fastest_addr]"
|
||||
MapConstLoadBalance DNSConfigUpstreamMode = "map[const:load_balance]"
|
||||
MapConstParallel DNSConfigUpstreamMode = "map[const:parallel]"
|
||||
)
|
||||
|
||||
// Defines values for DhcpSearchResultOtherServerFound.
|
||||
const (
|
||||
DhcpSearchResultOtherServerFoundError DhcpSearchResultOtherServerFound = "error"
|
||||
@@ -306,7 +314,7 @@ type Client struct {
|
||||
// changed.
|
||||
//
|
||||
// This behaviour can be changed in the future versions.
|
||||
UpstreamsCacheSize *int `json:"upstreams_cache_size,omitempty"`
|
||||
UpstreamsCacheSize *int `json:"upstreams_cache_size,omitempty"`
|
||||
UseGlobalBlockedServices *bool `json:"use_global_blocked_services,omitempty"`
|
||||
UseGlobalSettings *bool `json:"use_global_settings,omitempty"`
|
||||
}
|
||||
@@ -389,6 +397,17 @@ type ClientsFindEntry map[string]ClientFindSubEntry
|
||||
// ClientsFindResponse Client search results.
|
||||
type ClientsFindResponse = []ClientsFindEntry
|
||||
|
||||
// ClientsSearchRequest Client search request
|
||||
type ClientsSearchRequest struct {
|
||||
Clients *[]ClientsSearchRequestItem `json:"clients,omitempty"`
|
||||
}
|
||||
|
||||
// ClientsSearchRequestItem defines model for ClientsSearchRequestItem.
|
||||
type ClientsSearchRequestItem struct {
|
||||
// Id Client IP address, CIDR, MAC address, or ClientID
|
||||
Id *string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// DNSConfig DNS server configuration
|
||||
type DNSConfig struct {
|
||||
// BlockedResponseTtl TTL for blocked responses.
|
||||
@@ -431,15 +450,23 @@ type DNSConfig struct {
|
||||
ResolveClients *bool `json:"resolve_clients,omitempty"`
|
||||
|
||||
// UpstreamDns Upstream servers, port is optional after colon. Empty value will reset it to default values.
|
||||
UpstreamDns *[]string `json:"upstream_dns,omitempty"`
|
||||
UpstreamDnsFile *string `json:"upstream_dns_file,omitempty"`
|
||||
UpstreamMode *interface{} `json:"upstream_mode,omitempty"`
|
||||
UsePrivatePtrResolvers *bool `json:"use_private_ptr_resolvers,omitempty"`
|
||||
UpstreamDns *[]string `json:"upstream_dns,omitempty"`
|
||||
UpstreamDnsFile *string `json:"upstream_dns_file,omitempty"`
|
||||
|
||||
// UpstreamMode Upstream modes enumeration.
|
||||
UpstreamMode *DNSConfigUpstreamMode `json:"upstream_mode,omitempty"`
|
||||
|
||||
// UpstreamTimeout The number of seconds to wait for a response from the upstream server
|
||||
UpstreamTimeout *int `json:"upstream_timeout,omitempty"`
|
||||
UsePrivatePtrResolvers *bool `json:"use_private_ptr_resolvers,omitempty"`
|
||||
}
|
||||
|
||||
// DNSConfigBlockingMode defines model for DNSConfig.BlockingMode.
|
||||
type DNSConfigBlockingMode string
|
||||
|
||||
// DNSConfigUpstreamMode Upstream modes enumeration.
|
||||
type DNSConfigUpstreamMode string
|
||||
|
||||
// DayRange The single interval within a day. It begins at the `start` and ends before the `end`.
|
||||
type DayRange struct {
|
||||
// End The number of milliseconds elapsed from the start of a day. It is expected to be rounded to minutes. The maximum value is `86400000` (24 hours).
|
||||
@@ -712,11 +739,18 @@ type Login struct {
|
||||
// NetInterface Network interface info
|
||||
type NetInterface struct {
|
||||
// Flags Flags could be any combination of the following values, divided by the "|" character: "up", "broadcast", "loopback", "pointtopoint" and "multicast".
|
||||
Flags string `json:"flags"`
|
||||
HardwareAddress string `json:"hardware_address"`
|
||||
IpAddresses *[]string `json:"ip_addresses,omitempty"`
|
||||
Mtu int `json:"mtu"`
|
||||
Name string `json:"name"`
|
||||
Flags string `json:"flags"`
|
||||
|
||||
// GatewayIp The IP address of the gateway.
|
||||
GatewayIp string `json:"gateway_ip"`
|
||||
HardwareAddress string `json:"hardware_address"`
|
||||
|
||||
// Ipv4Addresses The addresses of the interface of v4 family.
|
||||
Ipv4Addresses []string `json:"ipv4_addresses"`
|
||||
|
||||
// Ipv6Addresses The addresses of the interface of v6 family.
|
||||
Ipv6Addresses []string `json:"ipv6_addresses"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// NetInterfaces Network interfaces dictionary, keys are interface names.
|
||||
@@ -891,6 +925,7 @@ type RewriteUpdate struct {
|
||||
type SafeSearchConfig struct {
|
||||
Bing *bool `json:"bing,omitempty"`
|
||||
Duckduckgo *bool `json:"duckduckgo,omitempty"`
|
||||
Ecosia *bool `json:"ecosia,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
Google *bool `json:"google,omitempty"`
|
||||
Pixabay *bool `json:"pixabay,omitempty"`
|
||||
@@ -1046,6 +1081,9 @@ type TlsConfig struct {
|
||||
// PrivateKeySaved Set to true if the user has previously saved a private key as a string. This is used so that the server and the client don't have to send the private key between each other every time, which might lead to security issues.
|
||||
PrivateKeySaved *bool `json:"private_key_saved,omitempty"`
|
||||
|
||||
// ServePlainDns Set to true if plain DNS is allowed for incoming requests.
|
||||
ServePlainDns *bool `json:"serve_plain_dns,omitempty"`
|
||||
|
||||
// ServerName server_name is the hostname of your HTTPS/TLS server
|
||||
ServerName *string `json:"server_name,omitempty"`
|
||||
|
||||
@@ -1188,6 +1226,9 @@ type ClientsAddJSONRequestBody = Client
|
||||
// ClientsDeleteJSONRequestBody defines body for ClientsDelete for application/json ContentType.
|
||||
type ClientsDeleteJSONRequestBody = ClientDelete
|
||||
|
||||
// ClientsSearchJSONRequestBody defines body for ClientsSearch for application/json ContentType.
|
||||
type ClientsSearchJSONRequestBody = ClientsSearchRequest
|
||||
|
||||
// ClientsUpdateJSONRequestBody defines body for ClientsUpdate for application/json ContentType.
|
||||
type ClientsUpdateJSONRequestBody = ClientUpdate
|
||||
|
||||
@@ -1477,6 +1518,11 @@ type ClientInterface interface {
|
||||
// ClientsFind request
|
||||
ClientsFind(ctx context.Context, params *ClientsFindParams, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
// ClientsSearchWithBody request with any body
|
||||
ClientsSearchWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
ClientsSearch(ctx context.Context, body ClientsSearchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
// ClientsUpdateWithBody request with any body
|
||||
ClientsUpdateWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
|
||||
|
||||
@@ -1969,6 +2015,30 @@ func (c *AdguardHomeClient) ClientsFind(ctx context.Context, params *ClientsFind
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *AdguardHomeClient) ClientsSearchWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewClientsSearchRequestWithBody(c.Server, contentType, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *AdguardHomeClient) ClientsSearch(ctx context.Context, body ClientsSearchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewClientsSearchRequest(c.Server, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
func (c *AdguardHomeClient) ClientsUpdateWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
|
||||
req, err := NewClientsUpdateRequestWithBody(c.Server, contentType, body)
|
||||
if err != nil {
|
||||
@@ -3657,6 +3727,46 @@ func NewClientsFindRequest(server string, params *ClientsFindParams) (*http.Requ
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// NewClientsSearchRequest calls the generic ClientsSearch builder with application/json body
|
||||
func NewClientsSearchRequest(server string, body ClientsSearchJSONRequestBody) (*http.Request, error) {
|
||||
var bodyReader io.Reader
|
||||
buf, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bodyReader = bytes.NewReader(buf)
|
||||
return NewClientsSearchRequestWithBody(server, "application/json", bodyReader)
|
||||
}
|
||||
|
||||
// NewClientsSearchRequestWithBody generates requests for ClientsSearch with any type of body
|
||||
func NewClientsSearchRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) {
|
||||
var err error
|
||||
|
||||
serverURL, err := url.Parse(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
operationPath := fmt.Sprintf("/clients/search")
|
||||
if operationPath[0] == '/' {
|
||||
operationPath = "." + operationPath
|
||||
}
|
||||
|
||||
queryURL, err := serverURL.Parse(operationPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", queryURL.String(), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", contentType)
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// NewClientsUpdateRequest calls the generic ClientsUpdate builder with application/json body
|
||||
func NewClientsUpdateRequest(server string, body ClientsUpdateJSONRequestBody) (*http.Request, error) {
|
||||
var bodyReader io.Reader
|
||||
@@ -5967,6 +6077,11 @@ type ClientWithResponsesInterface interface {
|
||||
// ClientsFindWithResponse request
|
||||
ClientsFindWithResponse(ctx context.Context, params *ClientsFindParams, reqEditors ...RequestEditorFn) (*ClientsFindResp, error)
|
||||
|
||||
// ClientsSearchWithBodyWithResponse request with any body
|
||||
ClientsSearchWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClientsSearchResp, error)
|
||||
|
||||
ClientsSearchWithResponse(ctx context.Context, body ClientsSearchJSONRequestBody, reqEditors ...RequestEditorFn) (*ClientsSearchResp, error)
|
||||
|
||||
// ClientsUpdateWithBodyWithResponse request with any body
|
||||
ClientsUpdateWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClientsUpdateResp, error)
|
||||
|
||||
@@ -6543,6 +6658,28 @@ func (r ClientsFindResp) StatusCode() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
type ClientsSearchResp struct {
|
||||
Body []byte
|
||||
HTTPResponse *http.Response
|
||||
JSON200 *ClientsFindResponse
|
||||
}
|
||||
|
||||
// Status returns HTTPResponse.Status
|
||||
func (r ClientsSearchResp) Status() string {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.Status
|
||||
}
|
||||
return http.StatusText(0)
|
||||
}
|
||||
|
||||
// StatusCode returns HTTPResponse.StatusCode
|
||||
func (r ClientsSearchResp) StatusCode() int {
|
||||
if r.HTTPResponse != nil {
|
||||
return r.HTTPResponse.StatusCode
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ClientsUpdateResp struct {
|
||||
Body []byte
|
||||
HTTPResponse *http.Response
|
||||
@@ -8082,6 +8219,23 @@ func (c *ClientWithResponses) ClientsFindWithResponse(ctx context.Context, param
|
||||
return ParseClientsFindResp(rsp)
|
||||
}
|
||||
|
||||
// ClientsSearchWithBodyWithResponse request with arbitrary body returning *ClientsSearchResp
|
||||
func (c *ClientWithResponses) ClientsSearchWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClientsSearchResp, error) {
|
||||
rsp, err := c.ClientsSearchWithBody(ctx, contentType, body, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseClientsSearchResp(rsp)
|
||||
}
|
||||
|
||||
func (c *ClientWithResponses) ClientsSearchWithResponse(ctx context.Context, body ClientsSearchJSONRequestBody, reqEditors ...RequestEditorFn) (*ClientsSearchResp, error) {
|
||||
rsp, err := c.ClientsSearch(ctx, body, reqEditors...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseClientsSearchResp(rsp)
|
||||
}
|
||||
|
||||
// ClientsUpdateWithBodyWithResponse request with arbitrary body returning *ClientsUpdateResp
|
||||
func (c *ClientWithResponses) ClientsUpdateWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClientsUpdateResp, error) {
|
||||
rsp, err := c.ClientsUpdateWithBody(ctx, contentType, body, reqEditors...)
|
||||
@@ -9227,6 +9381,32 @@ func ParseClientsFindResp(rsp *http.Response) (*ClientsFindResp, error) {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// ParseClientsSearchResp parses an HTTP response from a ClientsSearchWithResponse call
|
||||
func ParseClientsSearchResp(rsp *http.Response) (*ClientsSearchResp, error) {
|
||||
bodyBytes, err := io.ReadAll(rsp.Body)
|
||||
defer func() { _ = rsp.Body.Close() }()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &ClientsSearchResp{
|
||||
Body: bodyBytes,
|
||||
HTTPResponse: rsp,
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
|
||||
var dest ClientsFindResponse
|
||||
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.JSON200 = &dest
|
||||
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// ParseClientsUpdateResp parses an HTTP response from a ClientsUpdateWithResponse call
|
||||
func ParseClientsUpdateResp(rsp *http.Response) (*ClientsUpdateResp, error) {
|
||||
bodyBytes, err := io.ReadAll(rsp.Body)
|
||||
|
||||
103
pkg/client/model/model_private_test.go
Normal file
103
pkg/client/model/model_private_test.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/bakito/adguardhome-sync/pkg/log"
|
||||
"github.com/bakito/adguardhome-sync/pkg/utils"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
"github.com/onsi/gomega"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var _ = Describe("Types", func() {
|
||||
Context("DhcpConfigV4", func() {
|
||||
DescribeTable("DhcpConfigV4 should not be valid",
|
||||
func(v4 DhcpConfigV4) {
|
||||
gomega.Ω(v4.isValid()).Should(gomega.BeFalse())
|
||||
},
|
||||
Entry(`When GatewayIp is nil`, DhcpConfigV4{
|
||||
GatewayIp: nil,
|
||||
SubnetMask: utils.Ptr("2.2.2.2"),
|
||||
RangeStart: utils.Ptr("3.3.3.3"),
|
||||
RangeEnd: utils.Ptr("4.4.4.4"),
|
||||
}),
|
||||
Entry(`When GatewayIp is ""`, DhcpConfigV4{
|
||||
GatewayIp: utils.Ptr(""),
|
||||
SubnetMask: utils.Ptr("2.2.2.2"),
|
||||
RangeStart: utils.Ptr("3.3.3.3"),
|
||||
RangeEnd: utils.Ptr("4.4.4.4"),
|
||||
}),
|
||||
Entry(`When SubnetMask is nil`, DhcpConfigV4{
|
||||
GatewayIp: utils.Ptr("1.1.1.1"),
|
||||
SubnetMask: nil,
|
||||
RangeStart: utils.Ptr("3.3.3.3"),
|
||||
RangeEnd: utils.Ptr("4.4.4.4"),
|
||||
}),
|
||||
Entry(`When SubnetMask is ""`, DhcpConfigV4{
|
||||
GatewayIp: utils.Ptr("1.1.1.1"),
|
||||
SubnetMask: utils.Ptr(""),
|
||||
RangeStart: utils.Ptr("3.3.3.3"),
|
||||
RangeEnd: utils.Ptr("4.4.4.4"),
|
||||
}),
|
||||
Entry(`When SubnetMask is nil`, DhcpConfigV4{
|
||||
GatewayIp: utils.Ptr("1.1.1.1"),
|
||||
SubnetMask: utils.Ptr("2.2.2.2"),
|
||||
RangeStart: nil,
|
||||
RangeEnd: utils.Ptr("4.4.4.4"),
|
||||
}),
|
||||
Entry(`When SubnetMask is ""`, DhcpConfigV4{
|
||||
GatewayIp: utils.Ptr("1.1.1.1"),
|
||||
SubnetMask: utils.Ptr("2.2.2.2"),
|
||||
RangeStart: utils.Ptr(""),
|
||||
RangeEnd: utils.Ptr("4.4.4.4"),
|
||||
}),
|
||||
Entry(`When RangeEnd is nil`, DhcpConfigV4{
|
||||
GatewayIp: utils.Ptr("1.1.1.1"),
|
||||
SubnetMask: utils.Ptr("2.2.2.2"),
|
||||
RangeStart: utils.Ptr("3.3.3.3"),
|
||||
RangeEnd: nil,
|
||||
}),
|
||||
Entry(`When RangeEnd is ""`, DhcpConfigV4{
|
||||
GatewayIp: utils.Ptr("1.1.1.1"),
|
||||
SubnetMask: utils.Ptr("2.2.2.2"),
|
||||
RangeStart: utils.Ptr("3.3.3.3"),
|
||||
RangeEnd: utils.Ptr(""),
|
||||
}),
|
||||
)
|
||||
})
|
||||
Context("DhcpConfigV6", func() {
|
||||
DescribeTable("DhcpConfigV6 should not be valid",
|
||||
func(v6 DhcpConfigV6) {
|
||||
gomega.Ω(v6.isValid()).Should(gomega.BeFalse())
|
||||
},
|
||||
Entry(`When SubnetMask is nil`, DhcpConfigV6{RangeStart: nil}),
|
||||
Entry(`When SubnetMask is ""`, DhcpConfigV6{RangeStart: utils.Ptr("")}),
|
||||
)
|
||||
})
|
||||
Context("DNSConfig", func() {
|
||||
var (
|
||||
cfg *DNSConfig
|
||||
l *zap.SugaredLogger
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
cfg = &DNSConfig{
|
||||
UsePrivatePtrResolvers: utils.Ptr(true),
|
||||
}
|
||||
l = log.GetLogger("test")
|
||||
})
|
||||
Context("Sanitize", func() {
|
||||
It("should disable UsePrivatePtrResolvers resolvers is nil ", func() {
|
||||
cfg.LocalPtrUpstreams = nil
|
||||
cfg.Sanitize(l)
|
||||
gomega.Ω(cfg.UsePrivatePtrResolvers).ShouldNot(gomega.BeNil())
|
||||
gomega.Ω(*cfg.UsePrivatePtrResolvers).Should(gomega.Equal(false))
|
||||
})
|
||||
It("should disable UsePrivatePtrResolvers resolvers is empty ", func() {
|
||||
cfg.LocalPtrUpstreams = utils.Ptr([]string{})
|
||||
cfg.Sanitize(l)
|
||||
gomega.Ω(cfg.UsePrivatePtrResolvers).ShouldNot(gomega.BeNil())
|
||||
gomega.Ω(*cfg.UsePrivatePtrResolvers).Should(gomega.Equal(false))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -115,12 +115,12 @@ var _ = Describe("Types", func() {
|
||||
Context("QueryLogConfig", func() {
|
||||
Context("Equal", func() {
|
||||
var (
|
||||
a *model.QueryLogConfig
|
||||
b *model.QueryLogConfig
|
||||
a *model.QueryLogConfigWithIgnored
|
||||
b *model.QueryLogConfigWithIgnored
|
||||
)
|
||||
BeforeEach(func() {
|
||||
a = &model.QueryLogConfig{}
|
||||
b = &model.QueryLogConfig{}
|
||||
a = &model.QueryLogConfigWithIgnored{}
|
||||
b = &model.QueryLogConfigWithIgnored{}
|
||||
})
|
||||
It("should be equal", func() {
|
||||
a.Enabled = utils.Ptr(true)
|
||||
@@ -242,7 +242,10 @@ var _ = Describe("Types", func() {
|
||||
})
|
||||
It("should return 3 one replicas if urls are different", func() {
|
||||
cfg.Replica = &types.AdGuardInstance{URL: url, APIPath: apiPath}
|
||||
cfg.Replicas = []types.AdGuardInstance{{URL: url + "1", APIPath: apiPath}, {URL: url, APIPath: apiPath + "1"}}
|
||||
cfg.Replicas = []types.AdGuardInstance{
|
||||
{URL: url + "1", APIPath: apiPath},
|
||||
{URL: url, APIPath: apiPath + "1"},
|
||||
}
|
||||
r := cfg.UniqueReplicas()
|
||||
Ω(r).Should(HaveLen(3))
|
||||
})
|
||||
@@ -303,6 +306,29 @@ var _ = Describe("Types", func() {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("Client", func() {
|
||||
Context("Equals", func() {
|
||||
var (
|
||||
cl1 *model.Client
|
||||
cl2 *model.Client
|
||||
)
|
||||
BeforeEach(func() {
|
||||
cl1 = &model.Client{
|
||||
Name: utils.Ptr("foo"),
|
||||
BlockedServicesSchedule: &model.Schedule{TimeZone: utils.Ptr("UTC")},
|
||||
}
|
||||
cl2 = &model.Client{
|
||||
Name: utils.Ptr("foo"),
|
||||
BlockedServicesSchedule: &model.Schedule{TimeZone: utils.Ptr("Local")},
|
||||
}
|
||||
})
|
||||
|
||||
It("should equal if only timezone differs on empty blocked service schedule", func() {
|
||||
Ω(cl1.Equals(cl2)).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
Context("BlockedServices", func() {
|
||||
Context("Equals", func() {
|
||||
It("should be equal", func() {
|
||||
|
||||
175
pkg/config/config-schema.json
Normal file
175
pkg/config/config-schema.json
Normal file
@@ -0,0 +1,175 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Instance": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"apiPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"autoSetup": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"cookie": {
|
||||
"type": "string"
|
||||
},
|
||||
"dhcpServerEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"insecureSkipVerify": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"interfaceName": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"webURL": {
|
||||
"format": "uri",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"description": "validates only for valid schema. No required fields, as the can be defined via env ars afterwards.",
|
||||
"properties": {
|
||||
"api": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"darkMode": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"metrics": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"queryLogLimit": {
|
||||
"type": "integer"
|
||||
},
|
||||
"scrapeInterval": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"type": "number"
|
||||
},
|
||||
"tls": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"certDir": {
|
||||
"type": "string"
|
||||
},
|
||||
"certName": {
|
||||
"type": "string"
|
||||
},
|
||||
"keyName": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"continueOnError": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"cron": {
|
||||
"type": "string"
|
||||
},
|
||||
"features": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"clientSettings": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"dhcp": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"serverConfig": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"staticLeases": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"dns": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"accessLists": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"rewrites": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"serverConfig": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"filters": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"generalSettings": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"queryLogConfig": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"services": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"statsConfig": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"theme": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"origin": {
|
||||
"$ref": "#/definitions/Instance"
|
||||
},
|
||||
"printConfigOnly": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"replica": {
|
||||
"$ref": "#/definitions/Instance"
|
||||
},
|
||||
"replicas": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/Instance"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"runOnStart": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"title": "adguardhome-sync Configuration",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/log"
|
||||
"github.com/bakito/adguardhome-sync/pkg/types"
|
||||
"github.com/caarlos0/env/v10"
|
||||
"github.com/caarlos0/env/v11"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -20,6 +20,10 @@ func Get(configFile string, flags Flags) (*types.Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = validateSchema(path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := initialConfig()
|
||||
|
||||
// read yaml config
|
||||
@@ -32,15 +36,33 @@ func Get(configFile string, flags Flags) (*types.Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// overwrite from env vars
|
||||
// *bool field creates issues when already not nil
|
||||
cfg.Origin.DHCPServerEnabled = nil // origin filed makes no sense to be set.
|
||||
|
||||
if err := env.Parse(cfg); err != nil {
|
||||
// keep previously set value
|
||||
replicaDhcpServer := cfg.Replica.DHCPServerEnabled
|
||||
cfg.Replica.DHCPServerEnabled = nil
|
||||
|
||||
// ignore replicas form env parsing as they are handled separately
|
||||
replicas := cfg.Replicas
|
||||
cfg.Replicas = nil
|
||||
|
||||
// overwrite from env vars
|
||||
if err = env.Parse(cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := env.ParseWithOptions(&cfg.Origin, env.Options{Prefix: "ORIGIN_"}); err != nil {
|
||||
if err = env.ParseWithOptions(cfg.Replica, env.Options{Prefix: "REPLICA_"}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := env.ParseWithOptions(cfg.Replica, env.Options{Prefix: "REPLICA_"}); err != nil {
|
||||
// restore the replica
|
||||
cfg.Replicas = replicas
|
||||
|
||||
// if not set from env, use previous value
|
||||
if cfg.Replica.DHCPServerEnabled == nil {
|
||||
cfg.Replica.DHCPServerEnabled = replicaDhcpServer
|
||||
}
|
||||
|
||||
if err = env.ParseWithOptions(&cfg.Origin, env.Options{Prefix: "ORIGIN_"}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -5,22 +5,32 @@ import (
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/config"
|
||||
flagsmock "github.com/bakito/adguardhome-sync/pkg/mocks/flags"
|
||||
gm "github.com/golang/mock/gomock"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
gm "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
var _ = Describe("Config", func() {
|
||||
Context("Get", func() {
|
||||
var (
|
||||
flags *flagsmock.MockFlags
|
||||
mockCtrl *gm.Controller
|
||||
flags *flagsmock.MockFlags
|
||||
mockCtrl *gm.Controller
|
||||
changedEnvVars []string
|
||||
setEnv = func(name string, value string) {
|
||||
_ = os.Setenv(name, value)
|
||||
changedEnvVars = append(changedEnvVars, name)
|
||||
}
|
||||
)
|
||||
BeforeEach(func() {
|
||||
mockCtrl = gm.NewController(GinkgoT())
|
||||
flags = flagsmock.NewMockFlags(mockCtrl)
|
||||
changedEnvVars = nil
|
||||
})
|
||||
AfterEach(func() {
|
||||
for _, envVar := range changedEnvVars {
|
||||
_ = os.Unsetenv(envVar)
|
||||
println(envVar)
|
||||
}
|
||||
defer mockCtrl.Finish()
|
||||
})
|
||||
Context("Get", func() {
|
||||
@@ -51,10 +61,7 @@ var _ = Describe("Config", func() {
|
||||
Ω(cfg.Origin.URL).Should(Equal("https://origin-flag:443"))
|
||||
})
|
||||
It("should have the origin URL from the config env var", func() {
|
||||
os.Setenv("ORIGIN_URL", "https://origin-env:443")
|
||||
defer func() {
|
||||
_ = os.Unsetenv("ORIGIN_URL")
|
||||
}()
|
||||
setEnv("ORIGIN_URL", "https://origin-env:443")
|
||||
flags.EXPECT().Changed(config.FlagOriginURL).Return(true).AnyTimes()
|
||||
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
|
||||
flags.EXPECT().GetString(config.FlagOriginURL).Return("https://origin-flag:443", nil).AnyTimes()
|
||||
@@ -82,10 +89,7 @@ var _ = Describe("Config", func() {
|
||||
Ω(cfg.Replicas[0].InsecureSkipVerify).Should(BeTrue())
|
||||
})
|
||||
It("should have the insecure skip verify from the config env var", func() {
|
||||
os.Setenv("REPLICA_INSECURE_SKIP_VERIFY", "false")
|
||||
defer func() {
|
||||
_ = os.Unsetenv("REPLICA_INSECURE_SKIP_VERIFY")
|
||||
}()
|
||||
setEnv("REPLICA_INSECURE_SKIP_VERIFY", "false")
|
||||
flags.EXPECT().Changed(config.FlagReplicaISV).Return(true).AnyTimes()
|
||||
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
|
||||
flags.EXPECT().GetBool(config.FlagReplicaISV).Return(true, nil).AnyTimes()
|
||||
@@ -105,10 +109,7 @@ var _ = Describe("Config", func() {
|
||||
Ω(cfg.Replicas[0].InsecureSkipVerify).Should(BeFalse())
|
||||
})
|
||||
It("should have the insecure skip verify from the config env var", func() {
|
||||
os.Setenv("REPLICA1_INSECURE_SKIP_VERIFY", "true")
|
||||
defer func() {
|
||||
_ = os.Unsetenv("REPLICA1_INSECURE_SKIP_VERIFY")
|
||||
}()
|
||||
setEnv("REPLICA1_INSECURE_SKIP_VERIFY", "true")
|
||||
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
@@ -133,10 +134,7 @@ var _ = Describe("Config", func() {
|
||||
Ω(cfg.API.Port).Should(Equal(9990))
|
||||
})
|
||||
It("should have the api port from the config env var", func() {
|
||||
os.Setenv("API_PORT", "9999")
|
||||
defer func() {
|
||||
_ = os.Unsetenv("API_PORT")
|
||||
}()
|
||||
setEnv("API_PORT", "9999")
|
||||
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes()
|
||||
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
|
||||
flags.EXPECT().GetInt(config.FlagApiPort).Return(9990, nil).AnyTimes()
|
||||
@@ -146,6 +144,58 @@ var _ = Describe("Config", func() {
|
||||
Ω(cfg.API.Port).Should(Equal(9999))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Replica DHCPServerEnabled", func() {
|
||||
It("should have the dhcp server enabled from the config file", func() {
|
||||
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replica.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Replicas[0].DHCPServerEnabled).ShouldNot(BeNil())
|
||||
Ω(*cfg.Replicas[0].DHCPServerEnabled).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
Context("Replica 1 DHCPServerEnabled", func() {
|
||||
It("should have the dhcp server enabled from the config file", func() {
|
||||
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Replicas[0].DHCPServerEnabled).ShouldNot(BeNil())
|
||||
Ω(*cfg.Replicas[0].DHCPServerEnabled).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
Context("API Port", func() {
|
||||
It("should have the api port from the config file", func() {
|
||||
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.API.Port).Should(Equal(9090))
|
||||
})
|
||||
It("should have the api port from the config flags", func() {
|
||||
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes()
|
||||
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
|
||||
flags.EXPECT().GetInt(config.FlagApiPort).Return(9990, nil).AnyTimes()
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.API.Port).Should(Equal(9990))
|
||||
})
|
||||
It("should have the api port from the config env var", func() {
|
||||
setEnv("API_PORT", "9999")
|
||||
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes()
|
||||
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
|
||||
flags.EXPECT().GetInt(config.FlagApiPort).Return(9990, nil).AnyTimes()
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.API.Port).Should(Equal(9999))
|
||||
})
|
||||
})
|
||||
|
||||
//////
|
||||
|
||||
Context("Feature DNS Server Config", func() {
|
||||
It("should have the feature dns server config from the config file", func() {
|
||||
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
|
||||
@@ -163,10 +213,7 @@ var _ = Describe("Config", func() {
|
||||
Ω(cfg.Features.DNS.ServerConfig).Should(BeTrue())
|
||||
})
|
||||
It("should have the feature dns server config from the config env var", func() {
|
||||
os.Setenv("FEATURES_DNS_SERVER_CONFIG", "false")
|
||||
defer func() {
|
||||
_ = os.Unsetenv("FEATURES_DNS_SERVER_CONFIG")
|
||||
}()
|
||||
setEnv("FEATURES_DNS_SERVER_CONFIG", "false")
|
||||
flags.EXPECT().Changed(config.FlagFeatureDnsServerConfig).Return(true).AnyTimes()
|
||||
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
|
||||
flags.EXPECT().GetBool(config.FlagFeatureDnsServerConfig).Return(true, nil).AnyTimes()
|
||||
@@ -176,10 +223,7 @@ var _ = Describe("Config", func() {
|
||||
Ω(cfg.Features.DNS.ServerConfig).Should(BeFalse())
|
||||
})
|
||||
It("should have the feature dns server config from the config DEPRECATED env var", func() {
|
||||
os.Setenv("FEATURES_DNS_SERVERCONFIG", "false")
|
||||
defer func() {
|
||||
_ = os.Unsetenv("FEATURES_DNS_SERVERCONFIG")
|
||||
}()
|
||||
setEnv("FEATURES_DNS_SERVERCONFIG", "false")
|
||||
flags.EXPECT().Changed(config.FlagFeatureDnsServerConfig).Return(true).AnyTimes()
|
||||
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
|
||||
flags.EXPECT().GetBool(config.FlagFeatureDnsServerConfig).Return(true, nil).AnyTimes()
|
||||
|
||||
@@ -17,6 +17,7 @@ var envVars = []string{
|
||||
"FEATURES_CLIENT_SETTINGS",
|
||||
"FEATURES_SERVICES",
|
||||
"FEATURES_FILTERS",
|
||||
"FEATURES_THEME",
|
||||
"FEATURES_DHCP_SERVER_CONFIG",
|
||||
"FEATURES_DHCP_STATIC_LEASES",
|
||||
"FEATURES_DNS_SERVER_CONFIG",
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/types"
|
||||
"github.com/bakito/adguardhome-sync/pkg/utils"
|
||||
"github.com/caarlos0/env/v10"
|
||||
"github.com/caarlos0/env/v11"
|
||||
)
|
||||
|
||||
func handleDeprecatedEnvVars(cfg *types.Config) {
|
||||
@@ -107,9 +107,15 @@ func enrichReplicasFromEnv(initialReplicas []types.AdGuardInstance) ([]types.AdG
|
||||
for i := range replicas {
|
||||
reID := i + 1
|
||||
|
||||
// keep previously set value
|
||||
replicaDhcpServer := replicas[i].DHCPServerEnabled
|
||||
replicas[i].DHCPServerEnabled = nil
|
||||
if err := env.ParseWithOptions(&replicas[i], env.Options{Prefix: fmt.Sprintf("REPLICA%d_", reID)}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if replicas[i].DHCPServerEnabled == nil {
|
||||
replicas[i].DHCPServerEnabled = replicaDhcpServer
|
||||
}
|
||||
if val, ok := checkDeprecatedReplicaEnvVar("REPLICA%d_APIPATH", "REPLICA%d_API_PATH", reID); ok {
|
||||
replicas[i].APIPath = val
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func configFilePath(configFile string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(home, ".adguardhome-sync"), nil
|
||||
return filepath.Join(home, ".adguardhome-sync.yaml"), nil
|
||||
}
|
||||
return configFile, nil
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ var _ = Describe("Config", func() {
|
||||
result, err := configFilePath("")
|
||||
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(result).Should(Equal(filepath.Join(home, "/.adguardhome-sync")))
|
||||
Ω(result).Should(Equal(filepath.Join(home, "/.adguardhome-sync.yaml")))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
flagsmock "github.com/bakito/adguardhome-sync/pkg/mocks/flags"
|
||||
"github.com/bakito/adguardhome-sync/pkg/types"
|
||||
gm "github.com/golang/mock/gomock"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
gm "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
var _ = Describe("Config", func() {
|
||||
|
||||
52
pkg/config/validate.go
Normal file
52
pkg/config/validate.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/santhosh-tekuri/jsonschema/v6"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const schemaURL = "config-schema.json"
|
||||
|
||||
//go:embed config-schema.json
|
||||
var schemaData string
|
||||
|
||||
func validateSchema(cfgFile string) error {
|
||||
// ignore if file not exists
|
||||
if _, err := os.Stat(cfgFile); err != nil {
|
||||
return nil
|
||||
}
|
||||
// Load YAML file
|
||||
yamlContent, err := os.ReadFile(cfgFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return validateYAML(yamlContent)
|
||||
}
|
||||
|
||||
func validateYAML(yamlContent []byte) error {
|
||||
// Convert YAML to JSON
|
||||
var yamlData interface{}
|
||||
err := yaml.Unmarshal(yamlContent, &yamlData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load JSON schema
|
||||
sch, err := jsonschema.UnmarshalJSON(strings.NewReader(schemaData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c := jsonschema.NewCompiler()
|
||||
if err := c.AddResource(schemaURL, sch); err != nil {
|
||||
return err
|
||||
}
|
||||
schema := c.MustCompile(schemaURL)
|
||||
// validateSchema
|
||||
return schema.Validate(yamlData)
|
||||
}
|
||||
39
pkg/config/validate_test.go
Normal file
39
pkg/config/validate_test.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/bakito/adguardhome-sync/pkg/types"
|
||||
"github.com/go-faker/faker/v4"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var _ = Describe("Config", func() {
|
||||
Context("validateSchema", func() {
|
||||
DescribeTable("validateSchema config",
|
||||
func(configFile string, expectFail bool) {
|
||||
err := validateSchema(configFile)
|
||||
if expectFail {
|
||||
Ω(err).Should(HaveOccurred())
|
||||
} else {
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
}
|
||||
},
|
||||
Entry(`Should be valid`, "../../testdata/config/config-valid.yaml", false),
|
||||
Entry(`Should be valid if file doesn't exist`, "../../testdata/config/foo.bar", false),
|
||||
Entry(`Should fail if file is not yaml`, "../../go.mod", true),
|
||||
)
|
||||
It("validate config with all fields randomly populated", func() {
|
||||
cfg := &types.Config{}
|
||||
|
||||
err := faker.FakeData(cfg)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
|
||||
data, err := yaml.Marshal(&cfg)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
|
||||
err = validateYAML(data)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
const (
|
||||
logHistorySize = 50
|
||||
envLogLevel = "LOG_LEVEL"
|
||||
envLogFormat = "LOG_FORMAT"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -31,14 +32,23 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
format := "console"
|
||||
if fmt, ok := os.LookupEnv(envLogFormat); ok {
|
||||
format = fmt
|
||||
}
|
||||
|
||||
cfg := zap.Config{
|
||||
Level: zap.NewAtomicLevelAt(level),
|
||||
Development: false,
|
||||
Encoding: "console",
|
||||
EncoderConfig: zap.NewDevelopmentEncoderConfig(),
|
||||
Encoding: format,
|
||||
EncoderConfig: zap.NewProductionEncoderConfig(),
|
||||
OutputPaths: []string{"stdout"},
|
||||
ErrorOutputPaths: []string{"stderr"},
|
||||
}
|
||||
cfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
cfg.EncoderConfig.EncodeDuration = zapcore.StringDurationEncoder
|
||||
cfg.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
|
||||
|
||||
opt := zap.WrapCore(func(c zapcore.Core) zapcore.Core {
|
||||
return zapcore.NewTee(c, &logList{
|
||||
enc: zapcore.NewConsoleEncoder(cfg.EncoderConfig),
|
||||
@@ -96,6 +106,11 @@ func Logs() []string {
|
||||
return logs
|
||||
}
|
||||
|
||||
// Clear the current logs
|
||||
func Clear() {
|
||||
logs = nil
|
||||
}
|
||||
|
||||
func addFields(enc zapcore.ObjectEncoder, fields []zapcore.Field) {
|
||||
for i := range fields {
|
||||
fields[i].AddTo(enc)
|
||||
|
||||
14
pkg/metrics/handler.go
Normal file
14
pkg/metrics/handler.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
func Handler() gin.HandlerFunc {
|
||||
h := promhttp.Handler()
|
||||
|
||||
return func(c *gin.Context) {
|
||||
h.ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
}
|
||||
264
pkg/metrics/metrics.go
Normal file
264
pkg/metrics/metrics.go
Normal file
@@ -0,0 +1,264 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/bakito/adguardhome-sync/pkg/client/model"
|
||||
"github.com/bakito/adguardhome-sync/pkg/log"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const StatsTotal = "total"
|
||||
|
||||
var (
|
||||
l = log.GetLogger("metrics")
|
||||
|
||||
// avgProcessingTime - Average processing time for a DNS query
|
||||
avgProcessingTime = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "avg_processing_time",
|
||||
Namespace: "adguard",
|
||||
Help: "This represent the average processing time for a DNS query in s",
|
||||
},
|
||||
[]string{"hostname"},
|
||||
)
|
||||
|
||||
// dnsQueries - Number of DNS queries
|
||||
dnsQueries = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "num_dns_queries",
|
||||
Namespace: "adguard",
|
||||
Help: "Number of DNS queries",
|
||||
},
|
||||
[]string{"hostname"},
|
||||
)
|
||||
|
||||
// blockedFiltering - Number of DNS queries blocked
|
||||
blockedFiltering = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "num_blocked_filtering",
|
||||
Namespace: "adguard",
|
||||
Help: "This represent the number of domains blocked",
|
||||
},
|
||||
[]string{"hostname"},
|
||||
)
|
||||
|
||||
// parentalFiltering - Number of DNS queries replaced by parental control
|
||||
parentalFiltering = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "num_replaced_parental",
|
||||
Namespace: "adguard",
|
||||
Help: "This represent the number of domains blocked (parental)",
|
||||
},
|
||||
[]string{"hostname"},
|
||||
)
|
||||
|
||||
// safeBrowsingFiltering - Number of DNS queries replaced by safe browsing
|
||||
safeBrowsingFiltering = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "num_replaced_safebrowsing",
|
||||
Namespace: "adguard",
|
||||
Help: "This represent the number of domains blocked (safe browsing)",
|
||||
},
|
||||
[]string{"hostname"},
|
||||
)
|
||||
|
||||
// safeSearchFiltering - Number of DNS queries replaced by safe search
|
||||
safeSearchFiltering = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "num_replaced_safesearch",
|
||||
Namespace: "adguard",
|
||||
Help: "This represent the number of domains blocked (safe search)",
|
||||
},
|
||||
[]string{"hostname"},
|
||||
)
|
||||
|
||||
// topQueries - The number of top queries
|
||||
topQueries = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "top_queried_domains",
|
||||
Namespace: "adguard",
|
||||
Help: "This represent the top queried domains",
|
||||
},
|
||||
[]string{"hostname", "domain"},
|
||||
)
|
||||
|
||||
// topBlocked - The number of top domains blocked
|
||||
topBlocked = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "top_blocked_domains",
|
||||
Namespace: "adguard",
|
||||
Help: "This represent the top bloacked domains",
|
||||
},
|
||||
[]string{"hostname", "domain"},
|
||||
)
|
||||
|
||||
// topClients - The number of top clients
|
||||
topClients = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "top_clients",
|
||||
Namespace: "adguard",
|
||||
Help: "This represent the top clients",
|
||||
},
|
||||
[]string{"hostname", "client"},
|
||||
)
|
||||
|
||||
// queryTypes - The type of DNS Queries (A, AAAA...)
|
||||
queryTypes = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "query_types",
|
||||
Namespace: "adguard",
|
||||
Help: "This represent the DNS query types",
|
||||
},
|
||||
[]string{"hostname", "type"},
|
||||
)
|
||||
|
||||
// running - If Adguard is running
|
||||
running = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "running",
|
||||
Namespace: "adguard",
|
||||
Help: "This represent if Adguard is running",
|
||||
},
|
||||
[]string{"hostname"},
|
||||
)
|
||||
|
||||
// protectionEnabled - If Adguard protection is enabled
|
||||
protectionEnabled = prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "protection_enabled",
|
||||
Namespace: "adguard",
|
||||
Help: "This represent if Adguard Protection is enabled",
|
||||
},
|
||||
[]string{"hostname"},
|
||||
)
|
||||
|
||||
stats = OverallStats{}
|
||||
)
|
||||
|
||||
// Init initializes all Prometheus metrics made available by AdGuard exporter.
|
||||
func Init() {
|
||||
initMetric("avg_processing_time", avgProcessingTime)
|
||||
initMetric("num_dns_queries", dnsQueries)
|
||||
initMetric("num_blocked_filtering", blockedFiltering)
|
||||
initMetric("num_replaced_parental", parentalFiltering)
|
||||
initMetric("num_replaced_safebrowsing", safeBrowsingFiltering)
|
||||
initMetric("num_replaced_safesearch", safeSearchFiltering)
|
||||
initMetric("top_queried_domains", topQueries)
|
||||
initMetric("top_blocked_domains", topBlocked)
|
||||
initMetric("top_clients", topClients)
|
||||
initMetric("query_types", queryTypes)
|
||||
initMetric("running", running)
|
||||
initMetric("protection_enabled", protectionEnabled)
|
||||
}
|
||||
|
||||
func initMetric(name string, metric *prometheus.GaugeVec) {
|
||||
prometheus.MustRegister(metric)
|
||||
l.With("name", name).Info("New Prometheus metric registered")
|
||||
}
|
||||
|
||||
func Update(ims ...InstanceMetrics) {
|
||||
for _, im := range ims {
|
||||
update(im)
|
||||
stats[im.HostName] = im.Stats
|
||||
}
|
||||
|
||||
l.Debug("updated")
|
||||
}
|
||||
|
||||
func update(im InstanceMetrics) {
|
||||
// Status
|
||||
var isRunning int = 0
|
||||
if im.Status.Running {
|
||||
isRunning = 1
|
||||
}
|
||||
running.WithLabelValues(im.HostName).Set(float64(isRunning))
|
||||
|
||||
var isProtected int = 0
|
||||
if im.Status.ProtectionEnabled {
|
||||
isProtected = 1
|
||||
}
|
||||
protectionEnabled.WithLabelValues(im.HostName).Set(float64(isProtected))
|
||||
|
||||
// Stats
|
||||
avgProcessingTime.WithLabelValues(im.HostName).Set(safeMetric(im.Stats.AvgProcessingTime))
|
||||
dnsQueries.WithLabelValues(im.HostName).Set(safeMetric(im.Stats.NumDnsQueries))
|
||||
blockedFiltering.WithLabelValues(im.HostName).Set(safeMetric(im.Stats.NumBlockedFiltering))
|
||||
parentalFiltering.WithLabelValues(im.HostName).Set(safeMetric(im.Stats.NumReplacedParental))
|
||||
safeBrowsingFiltering.WithLabelValues(im.HostName).Set(safeMetric(im.Stats.NumReplacedSafebrowsing))
|
||||
safeSearchFiltering.WithLabelValues(im.HostName).Set(safeMetric(im.Stats.NumReplacedSafesearch))
|
||||
|
||||
if im.Stats.TopQueriedDomains != nil {
|
||||
for _, tq := range *im.Stats.TopQueriedDomains {
|
||||
for domain, value := range tq.AdditionalProperties {
|
||||
topQueries.WithLabelValues(im.HostName, domain).Set(float64(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
if im.Stats.TopBlockedDomains != nil {
|
||||
for _, tb := range *im.Stats.TopBlockedDomains {
|
||||
for domain, value := range tb.AdditionalProperties {
|
||||
topBlocked.WithLabelValues(im.HostName, domain).Set(float64(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
if im.Stats.TopClients != nil {
|
||||
for _, tc := range *im.Stats.TopClients {
|
||||
for source, value := range tc.AdditionalProperties {
|
||||
topClients.WithLabelValues(im.HostName, source).Set(float64(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LogQuery
|
||||
m := make(map[string]int)
|
||||
if im.QueryLog != nil && im.QueryLog.Data != nil {
|
||||
logdata := *im.QueryLog.Data
|
||||
for _, ld := range logdata {
|
||||
if ld.Answer != nil {
|
||||
dnsanswer := *ld.Answer
|
||||
if len(dnsanswer) > 0 {
|
||||
for _, dnsa := range dnsanswer {
|
||||
dnsType := *dnsa.Type
|
||||
m[dnsType] += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key, value := range m {
|
||||
queryTypes.WithLabelValues(im.HostName, key).Set(float64(value))
|
||||
}
|
||||
}
|
||||
|
||||
type InstanceMetrics struct {
|
||||
HostName string
|
||||
Status *model.ServerStatus
|
||||
Stats *model.Stats
|
||||
QueryLog *model.QueryLog
|
||||
}
|
||||
|
||||
type OverallStats map[string]*model.Stats
|
||||
|
||||
func (os OverallStats) consolidate() OverallStats {
|
||||
consolidated := OverallStats{StatsTotal: model.NewStats()}
|
||||
for host, stats := range os {
|
||||
consolidated[host] = stats
|
||||
consolidated[StatsTotal].Add(stats)
|
||||
}
|
||||
return consolidated
|
||||
}
|
||||
|
||||
func safeMetric[T int | float64 | float32](v *T) float64 {
|
||||
if v == nil {
|
||||
return 0
|
||||
}
|
||||
return float64(*v)
|
||||
}
|
||||
|
||||
func GetStats() OverallStats {
|
||||
return stats.consolidate()
|
||||
}
|
||||
|
||||
func (os OverallStats) Total() *model.Stats {
|
||||
return os[StatsTotal]
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/bakito/adguardhome-sync/pkg/client (interfaces: Client)
|
||||
//
|
||||
// Generated by this command:
|
||||
//
|
||||
// mockgen -package client -destination pkg/mocks/client/mock.go github.com/bakito/adguardhome-sync/pkg/client Client
|
||||
//
|
||||
|
||||
// Package client is a generated GoMock package.
|
||||
package client
|
||||
@@ -8,13 +13,14 @@ import (
|
||||
reflect "reflect"
|
||||
|
||||
model "github.com/bakito/adguardhome-sync/pkg/client/model"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
// MockClient is a mock of Client interface.
|
||||
type MockClient struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockClientMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockClientMockRecorder is the mock recorder for MockClient.
|
||||
@@ -50,52 +56,52 @@ func (mr *MockClientMockRecorder) AccessList() *gomock.Call {
|
||||
}
|
||||
|
||||
// AddClient mocks base method.
|
||||
func (m *MockClient) AddClient(arg0 *model.Client) error {
|
||||
func (m *MockClient) AddClient(client *model.Client) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AddClient", arg0)
|
||||
ret := m.ctrl.Call(m, "AddClient", client)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AddClient indicates an expected call of AddClient.
|
||||
func (mr *MockClientMockRecorder) AddClient(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) AddClient(client any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddClient", reflect.TypeOf((*MockClient)(nil).AddClient), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddClient", reflect.TypeOf((*MockClient)(nil).AddClient), client)
|
||||
}
|
||||
|
||||
// AddDHCPStaticLease mocks base method.
|
||||
func (m *MockClient) AddDHCPStaticLease(arg0 model.DhcpStaticLease) error {
|
||||
func (m *MockClient) AddDHCPStaticLease(lease model.DhcpStaticLease) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AddDHCPStaticLease", arg0)
|
||||
ret := m.ctrl.Call(m, "AddDHCPStaticLease", lease)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AddDHCPStaticLease indicates an expected call of AddDHCPStaticLease.
|
||||
func (mr *MockClientMockRecorder) AddDHCPStaticLease(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) AddDHCPStaticLease(lease any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDHCPStaticLease", reflect.TypeOf((*MockClient)(nil).AddDHCPStaticLease), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddDHCPStaticLease", reflect.TypeOf((*MockClient)(nil).AddDHCPStaticLease), lease)
|
||||
}
|
||||
|
||||
// AddFilter mocks base method.
|
||||
func (m *MockClient) AddFilter(arg0 bool, arg1 model.Filter) error {
|
||||
func (m *MockClient) AddFilter(whitelist bool, f model.Filter) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AddFilter", arg0, arg1)
|
||||
ret := m.ctrl.Call(m, "AddFilter", whitelist, f)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AddFilter indicates an expected call of AddFilter.
|
||||
func (mr *MockClientMockRecorder) AddFilter(arg0, arg1 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) AddFilter(whitelist, f any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFilter", reflect.TypeOf((*MockClient)(nil).AddFilter), arg0, arg1)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFilter", reflect.TypeOf((*MockClient)(nil).AddFilter), whitelist, f)
|
||||
}
|
||||
|
||||
// AddRewriteEntries mocks base method.
|
||||
func (m *MockClient) AddRewriteEntries(arg0 ...model.RewriteEntry) error {
|
||||
func (m *MockClient) AddRewriteEntries(e ...model.RewriteEntry) error {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []interface{}{}
|
||||
for _, a := range arg0 {
|
||||
varargs := []any{}
|
||||
for _, a := range e {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "AddRewriteEntries", varargs...)
|
||||
@@ -104,24 +110,9 @@ func (m *MockClient) AddRewriteEntries(arg0 ...model.RewriteEntry) error {
|
||||
}
|
||||
|
||||
// AddRewriteEntries indicates an expected call of AddRewriteEntries.
|
||||
func (mr *MockClientMockRecorder) AddRewriteEntries(arg0 ...interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) AddRewriteEntries(e ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRewriteEntries", reflect.TypeOf((*MockClient)(nil).AddRewriteEntries), arg0...)
|
||||
}
|
||||
|
||||
// BlockedServices mocks base method.
|
||||
func (m *MockClient) BlockedServices() (*[]string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "BlockedServices")
|
||||
ret0, _ := ret[0].(*[]string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// BlockedServices indicates an expected call of BlockedServices.
|
||||
func (mr *MockClientMockRecorder) BlockedServices() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BlockedServices", reflect.TypeOf((*MockClient)(nil).BlockedServices))
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRewriteEntries", reflect.TypeOf((*MockClient)(nil).AddRewriteEntries), e...)
|
||||
}
|
||||
|
||||
// BlockedServicesSchedule mocks base method.
|
||||
@@ -170,52 +161,52 @@ func (mr *MockClientMockRecorder) DNSConfig() *gomock.Call {
|
||||
}
|
||||
|
||||
// DeleteClient mocks base method.
|
||||
func (m *MockClient) DeleteClient(arg0 *model.Client) error {
|
||||
func (m *MockClient) DeleteClient(client *model.Client) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteClient", arg0)
|
||||
ret := m.ctrl.Call(m, "DeleteClient", client)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteClient indicates an expected call of DeleteClient.
|
||||
func (mr *MockClientMockRecorder) DeleteClient(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) DeleteClient(client any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteClient", reflect.TypeOf((*MockClient)(nil).DeleteClient), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteClient", reflect.TypeOf((*MockClient)(nil).DeleteClient), client)
|
||||
}
|
||||
|
||||
// DeleteDHCPStaticLease mocks base method.
|
||||
func (m *MockClient) DeleteDHCPStaticLease(arg0 model.DhcpStaticLease) error {
|
||||
func (m *MockClient) DeleteDHCPStaticLease(lease model.DhcpStaticLease) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteDHCPStaticLease", arg0)
|
||||
ret := m.ctrl.Call(m, "DeleteDHCPStaticLease", lease)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteDHCPStaticLease indicates an expected call of DeleteDHCPStaticLease.
|
||||
func (mr *MockClientMockRecorder) DeleteDHCPStaticLease(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) DeleteDHCPStaticLease(lease any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDHCPStaticLease", reflect.TypeOf((*MockClient)(nil).DeleteDHCPStaticLease), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDHCPStaticLease", reflect.TypeOf((*MockClient)(nil).DeleteDHCPStaticLease), lease)
|
||||
}
|
||||
|
||||
// DeleteFilter mocks base method.
|
||||
func (m *MockClient) DeleteFilter(arg0 bool, arg1 model.Filter) error {
|
||||
func (m *MockClient) DeleteFilter(whitelist bool, f model.Filter) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteFilter", arg0, arg1)
|
||||
ret := m.ctrl.Call(m, "DeleteFilter", whitelist, f)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteFilter indicates an expected call of DeleteFilter.
|
||||
func (mr *MockClientMockRecorder) DeleteFilter(arg0, arg1 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) DeleteFilter(whitelist, f any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFilter", reflect.TypeOf((*MockClient)(nil).DeleteFilter), arg0, arg1)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFilter", reflect.TypeOf((*MockClient)(nil).DeleteFilter), whitelist, f)
|
||||
}
|
||||
|
||||
// DeleteRewriteEntries mocks base method.
|
||||
func (m *MockClient) DeleteRewriteEntries(arg0 ...model.RewriteEntry) error {
|
||||
func (m *MockClient) DeleteRewriteEntries(e ...model.RewriteEntry) error {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []interface{}{}
|
||||
for _, a := range arg0 {
|
||||
varargs := []any{}
|
||||
for _, a := range e {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "DeleteRewriteEntries", varargs...)
|
||||
@@ -224,9 +215,9 @@ func (m *MockClient) DeleteRewriteEntries(arg0 ...model.RewriteEntry) error {
|
||||
}
|
||||
|
||||
// DeleteRewriteEntries indicates an expected call of DeleteRewriteEntries.
|
||||
func (mr *MockClientMockRecorder) DeleteRewriteEntries(arg0 ...interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) DeleteRewriteEntries(e ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRewriteEntries", reflect.TypeOf((*MockClient)(nil).DeleteRewriteEntries), arg0...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRewriteEntries", reflect.TypeOf((*MockClient)(nil).DeleteRewriteEntries), e...)
|
||||
}
|
||||
|
||||
// DhcpConfig mocks base method.
|
||||
@@ -303,11 +294,26 @@ func (mr *MockClientMockRecorder) ProfileInfo() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProfileInfo", reflect.TypeOf((*MockClient)(nil).ProfileInfo))
|
||||
}
|
||||
|
||||
// QueryLog mocks base method.
|
||||
func (m *MockClient) QueryLog(limit int) (*model.QueryLog, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "QueryLog", limit)
|
||||
ret0, _ := ret[0].(*model.QueryLog)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// QueryLog indicates an expected call of QueryLog.
|
||||
func (mr *MockClientMockRecorder) QueryLog(limit any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryLog", reflect.TypeOf((*MockClient)(nil).QueryLog), limit)
|
||||
}
|
||||
|
||||
// QueryLogConfig mocks base method.
|
||||
func (m *MockClient) QueryLogConfig() (*model.QueryLogConfig, error) {
|
||||
func (m *MockClient) QueryLogConfig() (*model.QueryLogConfigWithIgnored, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "QueryLogConfig")
|
||||
ret0, _ := ret[0].(*model.QueryLogConfig)
|
||||
ret0, _ := ret[0].(*model.QueryLogConfigWithIgnored)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -319,17 +325,17 @@ func (mr *MockClientMockRecorder) QueryLogConfig() *gomock.Call {
|
||||
}
|
||||
|
||||
// RefreshFilters mocks base method.
|
||||
func (m *MockClient) RefreshFilters(arg0 bool) error {
|
||||
func (m *MockClient) RefreshFilters(whitelist bool) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RefreshFilters", arg0)
|
||||
ret := m.ctrl.Call(m, "RefreshFilters", whitelist)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RefreshFilters indicates an expected call of RefreshFilters.
|
||||
func (mr *MockClientMockRecorder) RefreshFilters(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) RefreshFilters(whitelist any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshFilters", reflect.TypeOf((*MockClient)(nil).RefreshFilters), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshFilters", reflect.TypeOf((*MockClient)(nil).RefreshFilters), whitelist)
|
||||
}
|
||||
|
||||
// RewriteList mocks base method.
|
||||
@@ -386,51 +392,37 @@ func (m *MockClient) SetAccessList(arg0 *model.AccessList) error {
|
||||
}
|
||||
|
||||
// SetAccessList indicates an expected call of SetAccessList.
|
||||
func (mr *MockClientMockRecorder) SetAccessList(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) SetAccessList(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAccessList", reflect.TypeOf((*MockClient)(nil).SetAccessList), arg0)
|
||||
}
|
||||
|
||||
// SetBlockedServices mocks base method.
|
||||
func (m *MockClient) SetBlockedServices(arg0 *[]string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetBlockedServices", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SetBlockedServices indicates an expected call of SetBlockedServices.
|
||||
func (mr *MockClientMockRecorder) SetBlockedServices(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBlockedServices", reflect.TypeOf((*MockClient)(nil).SetBlockedServices), arg0)
|
||||
}
|
||||
|
||||
// SetBlockedServicesSchedule mocks base method.
|
||||
func (m *MockClient) SetBlockedServicesSchedule(arg0 *model.BlockedServicesSchedule) error {
|
||||
func (m *MockClient) SetBlockedServicesSchedule(schedule *model.BlockedServicesSchedule) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetBlockedServicesSchedule", arg0)
|
||||
ret := m.ctrl.Call(m, "SetBlockedServicesSchedule", schedule)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SetBlockedServicesSchedule indicates an expected call of SetBlockedServicesSchedule.
|
||||
func (mr *MockClientMockRecorder) SetBlockedServicesSchedule(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) SetBlockedServicesSchedule(schedule any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBlockedServicesSchedule", reflect.TypeOf((*MockClient)(nil).SetBlockedServicesSchedule), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBlockedServicesSchedule", reflect.TypeOf((*MockClient)(nil).SetBlockedServicesSchedule), schedule)
|
||||
}
|
||||
|
||||
// SetCustomRules mocks base method.
|
||||
func (m *MockClient) SetCustomRules(arg0 *[]string) error {
|
||||
func (m *MockClient) SetCustomRules(rules *[]string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetCustomRules", arg0)
|
||||
ret := m.ctrl.Call(m, "SetCustomRules", rules)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SetCustomRules indicates an expected call of SetCustomRules.
|
||||
func (mr *MockClientMockRecorder) SetCustomRules(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) SetCustomRules(rules any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCustomRules", reflect.TypeOf((*MockClient)(nil).SetCustomRules), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCustomRules", reflect.TypeOf((*MockClient)(nil).SetCustomRules), rules)
|
||||
}
|
||||
|
||||
// SetDNSConfig mocks base method.
|
||||
@@ -442,7 +434,7 @@ func (m *MockClient) SetDNSConfig(arg0 *model.DNSConfig) error {
|
||||
}
|
||||
|
||||
// SetDNSConfig indicates an expected call of SetDNSConfig.
|
||||
func (mr *MockClientMockRecorder) SetDNSConfig(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) SetDNSConfig(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDNSConfig", reflect.TypeOf((*MockClient)(nil).SetDNSConfig), arg0)
|
||||
}
|
||||
@@ -456,27 +448,27 @@ func (m *MockClient) SetDhcpConfig(arg0 *model.DhcpStatus) error {
|
||||
}
|
||||
|
||||
// SetDhcpConfig indicates an expected call of SetDhcpConfig.
|
||||
func (mr *MockClientMockRecorder) SetDhcpConfig(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) SetDhcpConfig(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDhcpConfig", reflect.TypeOf((*MockClient)(nil).SetDhcpConfig), arg0)
|
||||
}
|
||||
|
||||
// SetProfileInfo mocks base method.
|
||||
func (m *MockClient) SetProfileInfo(arg0 *model.ProfileInfo) error {
|
||||
func (m *MockClient) SetProfileInfo(settings *model.ProfileInfo) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetProfileInfo", arg0)
|
||||
ret := m.ctrl.Call(m, "SetProfileInfo", settings)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SetProfileInfo indicates an expected call of SetProfileInfo.
|
||||
func (mr *MockClientMockRecorder) SetProfileInfo(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) SetProfileInfo(settings any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetProfileInfo", reflect.TypeOf((*MockClient)(nil).SetProfileInfo), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetProfileInfo", reflect.TypeOf((*MockClient)(nil).SetProfileInfo), settings)
|
||||
}
|
||||
|
||||
// SetQueryLogConfig mocks base method.
|
||||
func (m *MockClient) SetQueryLogConfig(arg0 *model.QueryLogConfig) error {
|
||||
func (m *MockClient) SetQueryLogConfig(arg0 *model.QueryLogConfigWithIgnored) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetQueryLogConfig", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
@@ -484,37 +476,37 @@ func (m *MockClient) SetQueryLogConfig(arg0 *model.QueryLogConfig) error {
|
||||
}
|
||||
|
||||
// SetQueryLogConfig indicates an expected call of SetQueryLogConfig.
|
||||
func (mr *MockClientMockRecorder) SetQueryLogConfig(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) SetQueryLogConfig(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetQueryLogConfig", reflect.TypeOf((*MockClient)(nil).SetQueryLogConfig), arg0)
|
||||
}
|
||||
|
||||
// SetSafeSearchConfig mocks base method.
|
||||
func (m *MockClient) SetSafeSearchConfig(arg0 *model.SafeSearchConfig) error {
|
||||
func (m *MockClient) SetSafeSearchConfig(settings *model.SafeSearchConfig) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetSafeSearchConfig", arg0)
|
||||
ret := m.ctrl.Call(m, "SetSafeSearchConfig", settings)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SetSafeSearchConfig indicates an expected call of SetSafeSearchConfig.
|
||||
func (mr *MockClientMockRecorder) SetSafeSearchConfig(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) SetSafeSearchConfig(settings any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSafeSearchConfig", reflect.TypeOf((*MockClient)(nil).SetSafeSearchConfig), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSafeSearchConfig", reflect.TypeOf((*MockClient)(nil).SetSafeSearchConfig), settings)
|
||||
}
|
||||
|
||||
// SetStatsConfig mocks base method.
|
||||
func (m *MockClient) SetStatsConfig(arg0 *model.StatsConfig) error {
|
||||
func (m *MockClient) SetStatsConfig(sc *model.GetStatsConfigResponse) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetStatsConfig", arg0)
|
||||
ret := m.ctrl.Call(m, "SetStatsConfig", sc)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SetStatsConfig indicates an expected call of SetStatsConfig.
|
||||
func (mr *MockClientMockRecorder) SetStatsConfig(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) SetStatsConfig(sc any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetStatsConfig", reflect.TypeOf((*MockClient)(nil).SetStatsConfig), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetStatsConfig", reflect.TypeOf((*MockClient)(nil).SetStatsConfig), sc)
|
||||
}
|
||||
|
||||
// Setup mocks base method.
|
||||
@@ -531,11 +523,26 @@ func (mr *MockClientMockRecorder) Setup() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Setup", reflect.TypeOf((*MockClient)(nil).Setup))
|
||||
}
|
||||
|
||||
// Stats mocks base method.
|
||||
func (m *MockClient) Stats() (*model.Stats, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Stats")
|
||||
ret0, _ := ret[0].(*model.Stats)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Stats indicates an expected call of Stats.
|
||||
func (mr *MockClientMockRecorder) Stats() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stats", reflect.TypeOf((*MockClient)(nil).Stats))
|
||||
}
|
||||
|
||||
// StatsConfig mocks base method.
|
||||
func (m *MockClient) StatsConfig() (*model.StatsConfig, error) {
|
||||
func (m *MockClient) StatsConfig() (*model.GetStatsConfigResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "StatsConfig")
|
||||
ret0, _ := ret[0].(*model.StatsConfig)
|
||||
ret0, _ := ret[0].(*model.GetStatsConfigResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -562,85 +569,85 @@ func (mr *MockClientMockRecorder) Status() *gomock.Call {
|
||||
}
|
||||
|
||||
// ToggleFiltering mocks base method.
|
||||
func (m *MockClient) ToggleFiltering(arg0 bool, arg1 int) error {
|
||||
func (m *MockClient) ToggleFiltering(enabled bool, interval int) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ToggleFiltering", arg0, arg1)
|
||||
ret := m.ctrl.Call(m, "ToggleFiltering", enabled, interval)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// ToggleFiltering indicates an expected call of ToggleFiltering.
|
||||
func (mr *MockClientMockRecorder) ToggleFiltering(arg0, arg1 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) ToggleFiltering(enabled, interval any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleFiltering", reflect.TypeOf((*MockClient)(nil).ToggleFiltering), arg0, arg1)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleFiltering", reflect.TypeOf((*MockClient)(nil).ToggleFiltering), enabled, interval)
|
||||
}
|
||||
|
||||
// ToggleParental mocks base method.
|
||||
func (m *MockClient) ToggleParental(arg0 bool) error {
|
||||
func (m *MockClient) ToggleParental(enable bool) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ToggleParental", arg0)
|
||||
ret := m.ctrl.Call(m, "ToggleParental", enable)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// ToggleParental indicates an expected call of ToggleParental.
|
||||
func (mr *MockClientMockRecorder) ToggleParental(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) ToggleParental(enable any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleParental", reflect.TypeOf((*MockClient)(nil).ToggleParental), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleParental", reflect.TypeOf((*MockClient)(nil).ToggleParental), enable)
|
||||
}
|
||||
|
||||
// ToggleProtection mocks base method.
|
||||
func (m *MockClient) ToggleProtection(arg0 bool) error {
|
||||
func (m *MockClient) ToggleProtection(enable bool) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ToggleProtection", arg0)
|
||||
ret := m.ctrl.Call(m, "ToggleProtection", enable)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// ToggleProtection indicates an expected call of ToggleProtection.
|
||||
func (mr *MockClientMockRecorder) ToggleProtection(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) ToggleProtection(enable any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleProtection", reflect.TypeOf((*MockClient)(nil).ToggleProtection), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleProtection", reflect.TypeOf((*MockClient)(nil).ToggleProtection), enable)
|
||||
}
|
||||
|
||||
// ToggleSafeBrowsing mocks base method.
|
||||
func (m *MockClient) ToggleSafeBrowsing(arg0 bool) error {
|
||||
func (m *MockClient) ToggleSafeBrowsing(enable bool) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ToggleSafeBrowsing", arg0)
|
||||
ret := m.ctrl.Call(m, "ToggleSafeBrowsing", enable)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// ToggleSafeBrowsing indicates an expected call of ToggleSafeBrowsing.
|
||||
func (mr *MockClientMockRecorder) ToggleSafeBrowsing(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) ToggleSafeBrowsing(enable any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleSafeBrowsing", reflect.TypeOf((*MockClient)(nil).ToggleSafeBrowsing), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ToggleSafeBrowsing", reflect.TypeOf((*MockClient)(nil).ToggleSafeBrowsing), enable)
|
||||
}
|
||||
|
||||
// UpdateClient mocks base method.
|
||||
func (m *MockClient) UpdateClient(arg0 *model.Client) error {
|
||||
func (m *MockClient) UpdateClient(client *model.Client) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateClient", arg0)
|
||||
ret := m.ctrl.Call(m, "UpdateClient", client)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpdateClient indicates an expected call of UpdateClient.
|
||||
func (mr *MockClientMockRecorder) UpdateClient(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) UpdateClient(client any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateClient", reflect.TypeOf((*MockClient)(nil).UpdateClient), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateClient", reflect.TypeOf((*MockClient)(nil).UpdateClient), client)
|
||||
}
|
||||
|
||||
// UpdateFilter mocks base method.
|
||||
func (m *MockClient) UpdateFilter(arg0 bool, arg1 model.Filter) error {
|
||||
func (m *MockClient) UpdateFilter(whitelist bool, f model.Filter) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateFilter", arg0, arg1)
|
||||
ret := m.ctrl.Call(m, "UpdateFilter", whitelist, f)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpdateFilter indicates an expected call of UpdateFilter.
|
||||
func (mr *MockClientMockRecorder) UpdateFilter(arg0, arg1 interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) UpdateFilter(whitelist, f any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFilter", reflect.TypeOf((*MockClient)(nil).UpdateFilter), arg0, arg1)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateFilter", reflect.TypeOf((*MockClient)(nil).UpdateFilter), whitelist, f)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/bakito/adguardhome-sync/pkg/config (interfaces: Flags)
|
||||
//
|
||||
// Generated by this command:
|
||||
//
|
||||
// mockgen -package client -destination pkg/mocks/flags/mock.go github.com/bakito/adguardhome-sync/pkg/config Flags
|
||||
//
|
||||
|
||||
// Package client is a generated GoMock package.
|
||||
package client
|
||||
@@ -7,13 +12,14 @@ package client
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
// MockFlags is a mock of Flags interface.
|
||||
type MockFlags struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockFlagsMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// MockFlagsMockRecorder is the mock recorder for MockFlags.
|
||||
@@ -34,60 +40,60 @@ func (m *MockFlags) EXPECT() *MockFlagsMockRecorder {
|
||||
}
|
||||
|
||||
// Changed mocks base method.
|
||||
func (m *MockFlags) Changed(arg0 string) bool {
|
||||
func (m *MockFlags) Changed(name string) bool {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Changed", arg0)
|
||||
ret := m.ctrl.Call(m, "Changed", name)
|
||||
ret0, _ := ret[0].(bool)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Changed indicates an expected call of Changed.
|
||||
func (mr *MockFlagsMockRecorder) Changed(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockFlagsMockRecorder) Changed(name any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Changed", reflect.TypeOf((*MockFlags)(nil).Changed), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Changed", reflect.TypeOf((*MockFlags)(nil).Changed), name)
|
||||
}
|
||||
|
||||
// GetBool mocks base method.
|
||||
func (m *MockFlags) GetBool(arg0 string) (bool, error) {
|
||||
func (m *MockFlags) GetBool(name string) (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetBool", arg0)
|
||||
ret := m.ctrl.Call(m, "GetBool", name)
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetBool indicates an expected call of GetBool.
|
||||
func (mr *MockFlagsMockRecorder) GetBool(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockFlagsMockRecorder) GetBool(name any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBool", reflect.TypeOf((*MockFlags)(nil).GetBool), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBool", reflect.TypeOf((*MockFlags)(nil).GetBool), name)
|
||||
}
|
||||
|
||||
// GetInt mocks base method.
|
||||
func (m *MockFlags) GetInt(arg0 string) (int, error) {
|
||||
func (m *MockFlags) GetInt(name string) (int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetInt", arg0)
|
||||
ret := m.ctrl.Call(m, "GetInt", name)
|
||||
ret0, _ := ret[0].(int)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetInt indicates an expected call of GetInt.
|
||||
func (mr *MockFlagsMockRecorder) GetInt(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockFlagsMockRecorder) GetInt(name any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInt", reflect.TypeOf((*MockFlags)(nil).GetInt), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInt", reflect.TypeOf((*MockFlags)(nil).GetInt), name)
|
||||
}
|
||||
|
||||
// GetString mocks base method.
|
||||
func (m *MockFlags) GetString(arg0 string) (string, error) {
|
||||
func (m *MockFlags) GetString(name string) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetString", arg0)
|
||||
ret := m.ctrl.Call(m, "GetString", name)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetString indicates an expected call of GetString.
|
||||
func (mr *MockFlagsMockRecorder) GetString(arg0 interface{}) *gomock.Call {
|
||||
func (mr *MockFlagsMockRecorder) GetString(name any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetString", reflect.TypeOf((*MockFlags)(nil).GetString), arg0)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetString", reflect.TypeOf((*MockFlags)(nil).GetString), name)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ var (
|
||||
actionProfileInfo = func(ac *actionContext) error {
|
||||
if pro, err := ac.client.ProfileInfo(); err != nil {
|
||||
return err
|
||||
} else if merged := pro.ShouldSyncFor(ac.origin.profileInfo); merged != nil {
|
||||
} else if merged := pro.ShouldSyncFor(ac.origin.profileInfo, ac.cfg.Features.Theme); merged != nil {
|
||||
return ac.client.SetProfileInfo(merged)
|
||||
}
|
||||
return nil
|
||||
@@ -63,7 +63,7 @@ var (
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ac.origin.statsConfig.Interval != sc.Interval {
|
||||
if !sc.Equals(ac.origin.statsConfig) {
|
||||
return ac.client.SetStatsConfig(ac.origin.statsConfig)
|
||||
}
|
||||
return nil
|
||||
@@ -94,10 +94,10 @@ var (
|
||||
return err
|
||||
}
|
||||
|
||||
if err = syncFilterType(ac.rl, ac.origin.filters.Filters, rf.Filters, false, ac.client, ac.continueOnError); err != nil {
|
||||
if err = syncFilterType(ac.rl, ac.origin.filters.Filters, rf.Filters, false, ac.client, ac.cfg.ContinueOnError); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = syncFilterType(ac.rl, ac.origin.filters.WhitelistFilters, rf.WhitelistFilters, true, ac.client, ac.continueOnError); err != nil {
|
||||
if err = syncFilterType(ac.rl, ac.origin.filters.WhitelistFilters, rf.WhitelistFilters, true, ac.client, ac.cfg.ContinueOnError); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -105,23 +105,13 @@ var (
|
||||
return ac.client.SetCustomRules(ac.origin.filters.UserRules)
|
||||
}
|
||||
|
||||
if ac.origin.filters.Enabled != rf.Enabled || ac.origin.filters.Interval != rf.Interval {
|
||||
if !utils.PtrEquals(ac.origin.filters.Enabled, rf.Enabled) ||
|
||||
!utils.PtrEquals(ac.origin.filters.Interval, rf.Interval) {
|
||||
return ac.client.ToggleFiltering(*ac.origin.filters.Enabled, *ac.origin.filters.Interval)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
actionBlockedServices = func(ac *actionContext) error {
|
||||
rs, err := ac.client.BlockedServices()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !model.EqualsStringSlice(ac.origin.blockedServices, rs, true) {
|
||||
return ac.client.SetBlockedServices(ac.origin.blockedServices)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
actionBlockedServicesSchedule = func(ac *actionContext) error {
|
||||
rbss, err := ac.client.BlockedServicesSchedule()
|
||||
if err != nil {
|
||||
@@ -144,7 +134,7 @@ var (
|
||||
for _, client := range r {
|
||||
if err := ac.client.DeleteClient(client); err != nil {
|
||||
ac.rl.With("client-name", client.Name, "error", err).Error("error deleting client setting")
|
||||
if !ac.continueOnError {
|
||||
if !ac.cfg.ContinueOnError {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -153,7 +143,7 @@ var (
|
||||
for _, client := range a {
|
||||
if err := ac.client.AddClient(client); err != nil {
|
||||
ac.rl.With("client-name", client.Name, "error", err).Error("error adding client setting")
|
||||
if !ac.continueOnError {
|
||||
if !ac.cfg.ContinueOnError {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -162,7 +152,7 @@ var (
|
||||
for _, client := range u {
|
||||
if err := ac.client.UpdateClient(client); err != nil {
|
||||
ac.rl.With("client-name", client.Name, "error", err).Error("error updating client setting")
|
||||
if !ac.continueOnError {
|
||||
if !ac.cfg.ContinueOnError {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -186,6 +176,9 @@ var (
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// dc.Sanitize(ac.rl)
|
||||
|
||||
if !dc.Equals(ac.origin.dnsConfig) {
|
||||
if err = ac.client.SetDNSConfig(ac.origin.dnsConfig); err != nil {
|
||||
return err
|
||||
@@ -226,7 +219,7 @@ var (
|
||||
for _, lease := range r {
|
||||
if err := ac.client.DeleteDHCPStaticLease(lease); err != nil {
|
||||
ac.rl.With("hostname", lease.Hostname, "error", err).Error("error deleting dhcp static lease")
|
||||
if !ac.continueOnError {
|
||||
if !ac.cfg.ContinueOnError {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -235,7 +228,7 @@ var (
|
||||
for _, lease := range a {
|
||||
if err := ac.client.AddDHCPStaticLease(lease); err != nil {
|
||||
ac.rl.With("hostname", lease.Hostname, "error", err).Error("error adding dhcp static lease")
|
||||
if !ac.continueOnError {
|
||||
if !ac.cfg.ContinueOnError {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -244,7 +237,14 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func syncFilterType(rl *zap.SugaredLogger, of *[]model.Filter, rFilters *[]model.Filter, whitelist bool, replica client.Client, continueOnError bool) error {
|
||||
func syncFilterType(
|
||||
rl *zap.SugaredLogger,
|
||||
of *[]model.Filter,
|
||||
rFilters *[]model.Filter,
|
||||
whitelist bool,
|
||||
replica client.Client,
|
||||
continueOnError bool,
|
||||
) error {
|
||||
fa, fu, fd := model.MergeFilters(rFilters, of)
|
||||
|
||||
for _, f := range fd {
|
||||
|
||||
@@ -17,6 +17,11 @@ func setupActions(cfg *types.Config) (actions []syncAction) {
|
||||
action("safe browsing", actionSafeBrowsing),
|
||||
)
|
||||
}
|
||||
if cfg.Features.DNS.ServerConfig {
|
||||
actions = append(actions,
|
||||
action("DNS server config", actionDNSServerConfig),
|
||||
)
|
||||
}
|
||||
if cfg.Features.QueryLogConfig {
|
||||
actions = append(actions,
|
||||
action("query log config", actionQueryLogConfig),
|
||||
@@ -39,7 +44,6 @@ func setupActions(cfg *types.Config) (actions []syncAction) {
|
||||
}
|
||||
if cfg.Features.Services {
|
||||
actions = append(actions,
|
||||
action("blocked services", actionBlockedServices),
|
||||
action("blocked services schedule", actionBlockedServicesSchedule),
|
||||
)
|
||||
}
|
||||
@@ -53,12 +57,6 @@ func setupActions(cfg *types.Config) (actions []syncAction) {
|
||||
action("DNS access lists", actionDNSAccessLists),
|
||||
)
|
||||
}
|
||||
|
||||
if cfg.Features.DNS.ServerConfig {
|
||||
actions = append(actions,
|
||||
action("DNS server config", actionDNSServerConfig),
|
||||
)
|
||||
}
|
||||
if cfg.Features.DHCP.ServerConfig {
|
||||
actions = append(actions,
|
||||
action("DHCP server config", actionDHCPServerConfig),
|
||||
@@ -78,12 +76,12 @@ type syncAction interface {
|
||||
}
|
||||
|
||||
type actionContext struct {
|
||||
rl *zap.SugaredLogger
|
||||
origin *origin
|
||||
client client.Client
|
||||
replicaStatus *model.ServerStatus
|
||||
continueOnError bool
|
||||
replica types.AdGuardInstance
|
||||
rl *zap.SugaredLogger
|
||||
origin *origin
|
||||
client client.Client
|
||||
replicaStatus *model.ServerStatus
|
||||
replica types.AdGuardInstance
|
||||
cfg *types.Config
|
||||
}
|
||||
|
||||
type defaultAction struct {
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/log"
|
||||
"github.com/bakito/adguardhome-sync/pkg/metrics"
|
||||
"github.com/bakito/adguardhome-sync/version"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -32,11 +33,38 @@ func (w *worker) handleSync(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (w *worker) handleRoot(c *gin.Context) {
|
||||
total, dns, blocked, malware, adult := statsGraph()
|
||||
|
||||
c.HTML(http.StatusOK, "index.html", map[string]interface{}{
|
||||
"DarkMode": w.cfg.API.DarkMode,
|
||||
"Metrics": w.cfg.API.Metrics.Enabled,
|
||||
"Version": version.Version,
|
||||
"Build": version.Build,
|
||||
"SyncStatus": w.status(),
|
||||
"Stats": map[string]interface{}{
|
||||
"Labels": getLast24Hours(),
|
||||
"DNS": dns,
|
||||
"Blocked": blocked,
|
||||
"BlockedPercentage": fmt.Sprintf(
|
||||
"%.2f",
|
||||
(float64(*total.NumBlockedFiltering)*100.0)/float64(*total.NumDnsQueries),
|
||||
),
|
||||
"Malware": malware,
|
||||
"MalwarePercentage": fmt.Sprintf(
|
||||
"%.2f",
|
||||
(float64(*total.NumReplacedSafebrowsing)*100.0)/float64(*total.NumDnsQueries),
|
||||
),
|
||||
"Adult": adult,
|
||||
"AdultPercentage": fmt.Sprintf(
|
||||
"%.2f",
|
||||
(float64(*total.NumReplacedParental)*100.0)/float64(*total.NumDnsQueries),
|
||||
),
|
||||
|
||||
"TotalDNS": total.NumDnsQueries,
|
||||
"TotalBlocked": total.NumBlockedFiltering,
|
||||
"TotalMalware": total.NumReplacedSafebrowsing,
|
||||
"TotalAdult": total.NumReplacedParental,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -49,12 +77,22 @@ func (w *worker) handleLogs(c *gin.Context) {
|
||||
c.Data(http.StatusOK, "text/plain", []byte(strings.Join(log.Logs(), "")))
|
||||
}
|
||||
|
||||
func (w *worker) handleClearLogs(c *gin.Context) {
|
||||
log.Clear()
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
func (w *worker) handleStatus(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, w.status())
|
||||
}
|
||||
|
||||
func (w *worker) listenAndServe() {
|
||||
l.With("port", w.cfg.API.Port).Info("Starting API server")
|
||||
sl := l.With("port", w.cfg.API.Port)
|
||||
if w.cfg.API.TLS.Enabled() {
|
||||
c, k := w.cfg.API.TLS.Certs()
|
||||
sl = sl.With("tls-cert", c).With("tls-key", k)
|
||||
}
|
||||
sl.Info("Starting API server")
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
@@ -74,12 +112,25 @@ func (w *worker) listenAndServe() {
|
||||
r.SetHTMLTemplate(template.Must(template.New("index.html").Parse(string(index))))
|
||||
r.POST("/api/v1/sync", w.handleSync)
|
||||
r.GET("/api/v1/logs", w.handleLogs)
|
||||
r.POST("/api/v1/clear-logs", w.handleClearLogs)
|
||||
r.GET("/api/v1/status", w.handleStatus)
|
||||
r.GET("/favicon.ico", w.handleFavicon)
|
||||
r.GET("/", w.handleRoot)
|
||||
if w.cfg.API.Metrics.Enabled {
|
||||
r.GET("/metrics", metrics.Handler())
|
||||
|
||||
go w.startScraping()
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := httpServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||
var err error
|
||||
if w.cfg.API.TLS.Enabled() {
|
||||
err = httpServer.ListenAndServeTLS(w.cfg.API.TLS.Certs())
|
||||
} else {
|
||||
err = httpServer.ListenAndServe()
|
||||
}
|
||||
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
l.With("error", err).Fatalf("HTTP server ListenAndServe")
|
||||
}
|
||||
}()
|
||||
@@ -101,7 +152,7 @@ func (w *worker) listenAndServe() {
|
||||
l.Fatal("os.Kill - terminating...")
|
||||
}()
|
||||
|
||||
gracefullCtx, cancelShutdown := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
gracefulCtx, cancelShutdown := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancelShutdown()
|
||||
|
||||
if w.cron != nil {
|
||||
@@ -109,7 +160,7 @@ func (w *worker) listenAndServe() {
|
||||
w.cron.Stop()
|
||||
}
|
||||
|
||||
if err := httpServer.Shutdown(gracefullCtx); err != nil {
|
||||
if err := httpServer.Shutdown(gracefulCtx); err != nil {
|
||||
l.With("error", err).Error("Shutdown error")
|
||||
defer os.Exit(1)
|
||||
} else {
|
||||
@@ -123,8 +174,9 @@ func (w *worker) listenAndServe() {
|
||||
}
|
||||
|
||||
type syncStatus struct {
|
||||
Origin replicaStatus `json:"origin"`
|
||||
Replicas []replicaStatus `json:"replicas"`
|
||||
SyncRunning bool `json:"syncRunning"`
|
||||
Origin replicaStatus `json:"origin"`
|
||||
Replicas []replicaStatus `json:"replicas"`
|
||||
}
|
||||
|
||||
type replicaStatus struct {
|
||||
@@ -134,3 +186,26 @@ type replicaStatus struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
ProtectionEnabled *bool `json:"protection_enabled"`
|
||||
}
|
||||
|
||||
func getLast24Hours() []string {
|
||||
var result []string
|
||||
currentTime := time.Now()
|
||||
|
||||
// Loop to get the last 24 hours
|
||||
for i := 0; i < 24; i++ {
|
||||
// Calculate the time for the current hour in the loop
|
||||
timeInstance := currentTime.Add(time.Duration(-i) * time.Hour)
|
||||
timeInstance = timeInstance.Truncate(time.Hour)
|
||||
|
||||
// Format the time as "14 Dec 17:00"
|
||||
formattedTime := timeInstance.Format("02 Jan 15:04")
|
||||
result = append(result, formattedTime)
|
||||
}
|
||||
|
||||
// Reverse the slice to get the correct order (from oldest to latest)
|
||||
for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {
|
||||
result[i], result[j] = result[j], result[i]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>AdGuardHome sync</title>
|
||||
<script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js">
|
||||
<script type="text/javascript" src="https://code.jquery.com/jquery-3.7.1.min.js">
|
||||
</script>
|
||||
{{- if .DarkMode }}
|
||||
<link rel="stylesheet" href="https://bootswatch.com/5/darkly/bootstrap.min.css"
|
||||
crossorigin="anonymous">
|
||||
{{- else }}
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We"
|
||||
crossOrigin="anonymous">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
|
||||
crossorigin="anonymous">
|
||||
{{- end }}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
@@ -30,6 +30,12 @@
|
||||
}
|
||||
);
|
||||
});
|
||||
$("#clearLogs").click(function () {
|
||||
$.post("api/v1/clear-logs", {}, function () {
|
||||
$('#logs').html("");
|
||||
}
|
||||
);
|
||||
});
|
||||
$("#sync").click(function () {
|
||||
$.post("api/v1/sync", {}, function (data) {
|
||||
});
|
||||
@@ -39,6 +45,43 @@
|
||||
});
|
||||
</script>
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
<style>
|
||||
.stat-card {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
}
|
||||
{{- if .Metrics }}
|
||||
.stat-card h3 {
|
||||
margin: 0;
|
||||
font-size: 2rem;
|
||||
}
|
||||
.stat-card p {
|
||||
margin: 5px 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.percentage {
|
||||
font-size: 0.9rem;
|
||||
text-align: right;
|
||||
height: 20px;
|
||||
}
|
||||
canvas {
|
||||
flex-grow: 1;
|
||||
height: 100px !important;
|
||||
}
|
||||
{{- end }}
|
||||
.button-row {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.btn-group {
|
||||
margin: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid px-4">
|
||||
@@ -48,15 +91,60 @@
|
||||
<p class="h6">{{ .Version }} ({{ .Build }})</p>
|
||||
</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
{{- if .Metrics }}
|
||||
<div class="row g-4 d-flex">
|
||||
<div class="col-12 col-md-3 d-flex">
|
||||
<div class="stat-card flex-fill">
|
||||
<div class="percentage"></div>
|
||||
<h3 style="color: rgb(78, 141, 245);">{{.Stats.TotalDNS}}</h3>
|
||||
<p>DNS Queries</p>
|
||||
<canvas id="dnsQueriesChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-3 d-flex">
|
||||
<div class="stat-card flex-fill">
|
||||
<div class="percentage" style="color: rgb(255, 94, 94);">{{.Stats.BlockedPercentage}}%</div>
|
||||
<h3 style="color: rgb(255, 94, 94);">{{.Stats.TotalBlocked}}</h3>
|
||||
<p>Blocked by Filters</p>
|
||||
<canvas id="blockedFiltersChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-3 d-flex">
|
||||
<div class="stat-card flex-fill">
|
||||
<div class="percentage" style="color: rgb(110, 224, 122);">{{.Stats.MalwarePercentage}}%</div>
|
||||
<h3 style="color: rgb(110, 224, 122);">{{.Stats.TotalMalware}}</h3>
|
||||
<p>Blocked malware/phishing</p>
|
||||
<canvas id="malwareChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-3 d-flex">
|
||||
<div class="stat-card flex-fill">
|
||||
<div class="percentage" style="color: rgb(232, 198, 78);">{{.Stats.AdultPercentage}}%</div>
|
||||
<h3 style="color: rgb(232, 198, 78);">{{.Stats.TotalAdult}}</h3>
|
||||
<p>Blocked adult websites</p>
|
||||
<canvas id="adultWebsitesChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{- end }}
|
||||
<div class="row button-row">
|
||||
<div class="col">
|
||||
<div class="btn-group" role="group">
|
||||
<input class="btn btn-success" type="button" id="sync" value="Synchronize"/>
|
||||
<input class="btn btn-secondary" type="button" id="showLogs" value="Update Logs"/>
|
||||
<button type="button" class="btn btn-success" id="sync">Synchronize</button>
|
||||
<button type="button" class="btn btn-secondary" id="showLogs">Update Logs</button>
|
||||
<button type="button" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#" id="clearLogs">Clear Logs</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-md-auto">
|
||||
<div class="float-right">
|
||||
<div class="btn-group float-right" role="group">
|
||||
<a href="{{ .SyncStatus.Origin.URL }}" target="_blank" class="btn btn-{{ .SyncStatus.Origin.Status }}"
|
||||
type="button" id="origin"
|
||||
{{ if .SyncStatus.Origin.Error }} title="{{ .SyncStatus.Origin.Error }}" {{ end }}>Origin {{ .SyncStatus.Origin.Host }}</a>
|
||||
@@ -69,10 +157,97 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<pre class="p-3 border"><code id="logs"></code></pre>
|
||||
<div class="col-12 col-md-12">
|
||||
<div class="stat-card">
|
||||
<pre class="p-3 border"><code id="logs"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- openssl dgst -sha384 -binary popper.min.js | openssl base64 -A -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"
|
||||
integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous">
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"
|
||||
integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous">
|
||||
</script>
|
||||
{{- if .Metrics }}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"ŝ
|
||||
integrity="sha384-vsrfeLOOY6KuIYKDlmVH5UiBmgIdB1oEf7p01YgWHuqmOHfZr374+odEv96n9tNC" crossorigin="anonymous">
|
||||
</script>
|
||||
<script>
|
||||
// Function to create minimal line charts
|
||||
function createChart(canvasId, data) {
|
||||
const ctx = document.getElementById(canvasId).getContext('2d');
|
||||
|
||||
const datasets = Array(data.length);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
datasets[i] = {
|
||||
data: data[i].data,
|
||||
title: data[i].title,
|
||||
backgroundColor: `rgb(${data[i].r}, ${data[i].g}, ${data[i].b}, 0.2)`,
|
||||
borderColor: `rgb(${data[i].r}, ${data[i].g}, ${data[i].b}, 1)`,
|
||||
borderWidth: 3,
|
||||
fill: data[i].fill,
|
||||
pointRadius: 0,
|
||||
}
|
||||
}
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: {{.Stats.Labels}},
|
||||
datasets: datasets
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
bodyFont: {
|
||||
size: 20
|
||||
},
|
||||
titleFont: {
|
||||
size: 20
|
||||
},
|
||||
displayColors: false,
|
||||
callbacks: {
|
||||
label: function(tooltipItem) {
|
||||
if (tooltipItem.dataset.title) {
|
||||
return tooltipItem.raw + " - " + tooltipItem.dataset.title;
|
||||
}
|
||||
return tooltipItem.raw;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: { display: false,
|
||||
title: {
|
||||
display: true
|
||||
}
|
||||
},
|
||||
y: { display: false,
|
||||
min: 0,
|
||||
title: {
|
||||
display: true
|
||||
} }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createChart('dnsQueriesChart', {{.Stats.DNS}});
|
||||
createChart('blockedFiltersChart', {{.Stats.Blocked}});
|
||||
createChart('malwareChart', {{.Stats.Malware}});
|
||||
createChart('adultWebsitesChart', {{.Stats.Adult}});
|
||||
</script>
|
||||
{{- end }}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
50
pkg/sync/scrape.go
Normal file
50
pkg/sync/scrape.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/metrics"
|
||||
"github.com/bakito/adguardhome-sync/pkg/types"
|
||||
)
|
||||
|
||||
func (w *worker) startScraping() {
|
||||
metrics.Init()
|
||||
if w.cfg.API.Metrics.ScrapeInterval == 0 {
|
||||
w.cfg.API.Metrics.ScrapeInterval = 30 * time.Second
|
||||
}
|
||||
if w.cfg.API.Metrics.QueryLogLimit == 0 {
|
||||
w.cfg.API.Metrics.QueryLogLimit = 10_000
|
||||
}
|
||||
l.With(
|
||||
"scrape-interval", w.cfg.API.Metrics.ScrapeInterval,
|
||||
"query-log-limit", w.cfg.API.Metrics.QueryLogLimit,
|
||||
).Info("setup metrics")
|
||||
w.scrape()
|
||||
for range time.Tick(w.cfg.API.Metrics.ScrapeInterval) {
|
||||
w.scrape()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *worker) scrape() {
|
||||
var ims []metrics.InstanceMetrics
|
||||
|
||||
ims = append(ims, w.getMetrics(w.cfg.Origin))
|
||||
for _, replica := range w.cfg.Replicas {
|
||||
ims = append(ims, w.getMetrics(replica))
|
||||
}
|
||||
metrics.Update(ims...)
|
||||
}
|
||||
|
||||
func (w *worker) getMetrics(inst types.AdGuardInstance) (im metrics.InstanceMetrics) {
|
||||
client, err := w.createClient(inst)
|
||||
if err != nil {
|
||||
l.With("error", err, "url", w.cfg.Origin.URL).Error("Error creating origin client")
|
||||
return
|
||||
}
|
||||
|
||||
im.HostName = inst.Host
|
||||
im.Status, _ = client.Status()
|
||||
im.Stats, _ = client.Stats()
|
||||
im.QueryLog, _ = client.QueryLog(w.cfg.API.Metrics.QueryLogLimit)
|
||||
return
|
||||
}
|
||||
129
pkg/sync/stats.go
Normal file
129
pkg/sync/stats.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/client/model"
|
||||
"github.com/bakito/adguardhome-sync/pkg/metrics"
|
||||
)
|
||||
|
||||
var (
|
||||
blue = []int{78, 141, 245}
|
||||
blueAlternatives = [][]int{
|
||||
{44, 95, 163},
|
||||
{122, 166, 247},
|
||||
{30, 61, 92},
|
||||
{93, 158, 255},
|
||||
{58, 123, 213},
|
||||
}
|
||||
|
||||
red = []int{255, 94, 94}
|
||||
redAlternatives = [][]int{
|
||||
{204, 59, 59},
|
||||
{255, 127, 127},
|
||||
{140, 36, 36},
|
||||
{255, 153, 153},
|
||||
{255, 66, 66},
|
||||
}
|
||||
|
||||
yellow = []int{232, 198, 78}
|
||||
yellowAlternatives = [][]int{
|
||||
{196, 163, 60},
|
||||
{255, 220, 110},
|
||||
{140, 114, 36},
|
||||
{250, 233, 156},
|
||||
{212, 180, 84},
|
||||
}
|
||||
|
||||
green = []int{110, 224, 122}
|
||||
greenAlternatives = [][]int{
|
||||
{68, 160, 80},
|
||||
{142, 255, 158},
|
||||
{44, 140, 63},
|
||||
{163, 255, 192},
|
||||
{85, 198, 102},
|
||||
}
|
||||
)
|
||||
|
||||
func statsGraph() (*model.Stats, []line, []line, []line, []line) {
|
||||
s := metrics.GetStats()
|
||||
t := s.Total()
|
||||
dns := graphLines(t, s, blue, blueAlternatives, func(s *model.Stats) []int {
|
||||
return safeStats(s.DnsQueries)
|
||||
})
|
||||
blocked := graphLines(t, s, red, redAlternatives, func(s *model.Stats) []int {
|
||||
return safeStats(s.BlockedFiltering)
|
||||
})
|
||||
malware := graphLines(t, s, green, greenAlternatives, func(s *model.Stats) []int {
|
||||
return safeStats(s.ReplacedSafebrowsing)
|
||||
})
|
||||
adult := graphLines(t, s, yellow, yellowAlternatives, func(s *model.Stats) []int {
|
||||
return safeStats(s.ReplacedParental)
|
||||
})
|
||||
|
||||
return t, dns, blocked, malware, adult
|
||||
}
|
||||
|
||||
func safeStats(stats *[]int) []int {
|
||||
if stats == nil {
|
||||
return make([]int, 0)
|
||||
}
|
||||
return *stats
|
||||
}
|
||||
|
||||
func graphLines(
|
||||
t *model.Stats,
|
||||
s metrics.OverallStats,
|
||||
baseColor []int,
|
||||
altColors [][]int,
|
||||
dataCB func(s *model.Stats) []int,
|
||||
) []line {
|
||||
g := &graph{
|
||||
total: line{
|
||||
Fill: true,
|
||||
Title: "Total",
|
||||
Data: dataCB(t),
|
||||
R: baseColor[0],
|
||||
G: baseColor[1],
|
||||
B: baseColor[2],
|
||||
},
|
||||
}
|
||||
|
||||
var i int
|
||||
for name, data := range s {
|
||||
if name != metrics.StatsTotal {
|
||||
g.replicas = append(g.replicas, line{
|
||||
Fill: false,
|
||||
Title: name,
|
||||
Data: dataCB(data),
|
||||
R: altColors[i%len(altColors)][0],
|
||||
G: altColors[i%len(altColors)][1],
|
||||
B: altColors[i%len(altColors)][2],
|
||||
})
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
lines := []line{g.total}
|
||||
|
||||
slices.SortFunc(g.replicas, func(a, b line) int {
|
||||
return strings.Compare(a.Title, b.Title)
|
||||
})
|
||||
lines = append(lines, g.replicas...)
|
||||
return lines
|
||||
}
|
||||
|
||||
type graph struct {
|
||||
total line
|
||||
replicas []line
|
||||
}
|
||||
|
||||
type line struct {
|
||||
Data []int `json:"data"`
|
||||
R int `json:"r"`
|
||||
G int `json:"g"`
|
||||
B int `json:"b"`
|
||||
Title string `json:"title"`
|
||||
Fill bool `json:"fill"`
|
||||
}
|
||||
@@ -66,16 +66,12 @@ func Sync(cfg *types.Config) error {
|
||||
if cfg.API.Port != 0 {
|
||||
w.cron.Start()
|
||||
} else {
|
||||
runOnStartAsync(cfg, w)
|
||||
w.cron.Run()
|
||||
}
|
||||
}
|
||||
if cfg.API.Port != 0 {
|
||||
if cfg.RunOnStart {
|
||||
go func() {
|
||||
l.Info("Running sync on startup")
|
||||
w.sync()
|
||||
}()
|
||||
}
|
||||
runOnStartAsync(cfg, w)
|
||||
w.listenAndServe()
|
||||
} else if cfg.RunOnStart {
|
||||
l.Info("Running sync on startup")
|
||||
@@ -85,6 +81,15 @@ func Sync(cfg *types.Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func runOnStartAsync(cfg *types.Config, w *worker) {
|
||||
if cfg.RunOnStart {
|
||||
go func() {
|
||||
l.Info("Running sync on startup")
|
||||
w.sync()
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
type worker struct {
|
||||
cfg *types.Config
|
||||
running bool
|
||||
@@ -110,6 +115,8 @@ func (w *worker) status() *syncStatus {
|
||||
return syncStatus.Replicas[i].Host < syncStatus.Replicas[j].Host
|
||||
})
|
||||
|
||||
syncStatus.SyncRunning = w.running
|
||||
|
||||
return syncStatus
|
||||
}
|
||||
|
||||
@@ -165,7 +172,8 @@ func (w *worker) sync() {
|
||||
}
|
||||
|
||||
if versions.IsNewerThan(versions.MinAgh, o.status.Version) {
|
||||
sl.With("error", err, "version", o.status.Version).Errorf("Origin AdGuard Home version must be >= %s", versions.MinAgh)
|
||||
sl.With("error", err, "version", o.status.Version).
|
||||
Errorf("Origin AdGuard Home version must be >= %s", versions.MinAgh)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -199,12 +207,6 @@ func (w *worker) sync() {
|
||||
return
|
||||
}
|
||||
|
||||
o.blockedServices, err = oc.BlockedServices()
|
||||
if err != nil {
|
||||
sl.With("error", err).Error("Error getting origin blocked services")
|
||||
return
|
||||
}
|
||||
|
||||
o.blockedServicesSchedule, err = oc.BlockedServicesSchedule()
|
||||
if err != nil {
|
||||
sl.With("error", err).Error("Error getting origin blocked services schedule")
|
||||
@@ -279,21 +281,23 @@ func (w *worker) syncTo(l *zap.SugaredLogger, o *origin, replica types.AdGuardIn
|
||||
rl.With("version", replicaStatus.Version).Info("Connected to replica")
|
||||
|
||||
if versions.IsNewerThan(versions.MinAgh, replicaStatus.Version) {
|
||||
rl.With("error", err, "version", replicaStatus.Version).Errorf("Replica AdGuard Home version must be >= %s", versions.MinAgh)
|
||||
rl.With("error", err, "version", replicaStatus.Version).
|
||||
Errorf("Replica AdGuard Home version must be >= %s", versions.MinAgh)
|
||||
return
|
||||
}
|
||||
|
||||
if o.status.Version != replicaStatus.Version {
|
||||
rl.With("originVersion", o.status.Version, "replicaVersion", replicaStatus.Version).Warn("Versions do not match")
|
||||
rl.With("originVersion", o.status.Version, "replicaVersion", replicaStatus.Version).
|
||||
Warn("Versions do not match")
|
||||
}
|
||||
|
||||
ac := &actionContext{
|
||||
continueOnError: w.cfg.ContinueOnError,
|
||||
rl: rl,
|
||||
origin: o,
|
||||
replicaStatus: replicaStatus,
|
||||
client: rc,
|
||||
replica: replica,
|
||||
cfg: w.cfg,
|
||||
rl: rl,
|
||||
origin: o,
|
||||
replicaStatus: replicaStatus,
|
||||
client: rc,
|
||||
replica: replica,
|
||||
}
|
||||
for _, action := range w.actions {
|
||||
if err := action.sync(ac); err != nil {
|
||||
@@ -307,7 +311,11 @@ func (w *worker) syncTo(l *zap.SugaredLogger, o *origin, replica types.AdGuardIn
|
||||
rl.Info("Sync done")
|
||||
}
|
||||
|
||||
func (w *worker) statusWithSetup(rl *zap.SugaredLogger, replica types.AdGuardInstance, rc client.Client) (*model.ServerStatus, error) {
|
||||
func (w *worker) statusWithSetup(
|
||||
rl *zap.SugaredLogger,
|
||||
replica types.AdGuardInstance,
|
||||
rc client.Client,
|
||||
) (*model.ServerStatus, error) {
|
||||
rs, err := rc.Status()
|
||||
if err != nil {
|
||||
if replica.AutoSetup && errors.Is(err, client.ErrSetupNeeded) {
|
||||
@@ -325,12 +333,11 @@ func (w *worker) statusWithSetup(rl *zap.SugaredLogger, replica types.AdGuardIns
|
||||
type origin struct {
|
||||
status *model.ServerStatus
|
||||
rewrites *model.RewriteEntries
|
||||
blockedServices *model.BlockedServicesArray
|
||||
blockedServicesSchedule *model.BlockedServicesSchedule
|
||||
filters *model.FilterStatus
|
||||
clients *model.Clients
|
||||
queryLogConfig *model.QueryLogConfig
|
||||
statsConfig *model.StatsConfig
|
||||
queryLogConfig *model.QueryLogConfigWithIgnored
|
||||
statsConfig *model.GetStatsConfigResponse
|
||||
accessList *model.AccessList
|
||||
dnsConfig *model.DNSConfig
|
||||
dhcpServerConfig *model.DhcpStatus
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
"github.com/bakito/adguardhome-sync/pkg/types"
|
||||
"github.com/bakito/adguardhome-sync/pkg/utils"
|
||||
"github.com/bakito/adguardhome-sync/pkg/versions"
|
||||
gm "github.com/golang/mock/gomock"
|
||||
"github.com/google/uuid"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
gm "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
var _ = Describe("Sync", func() {
|
||||
@@ -48,6 +48,7 @@ var _ = Describe("Sync", func() {
|
||||
GeneralSettings: true,
|
||||
StatsConfig: true,
|
||||
QueryLogConfig: true,
|
||||
Theme: true,
|
||||
},
|
||||
Replicas: []types.AdGuardInstance{
|
||||
{},
|
||||
@@ -57,8 +58,8 @@ var _ = Describe("Sync", func() {
|
||||
te = errors.New(uuid.NewString())
|
||||
|
||||
ac = &actionContext{
|
||||
continueOnError: false,
|
||||
rl: l,
|
||||
cfg: w.cfg,
|
||||
rl: l,
|
||||
origin: &origin{
|
||||
profileInfo: &model.ProfileInfo{
|
||||
Name: "origin",
|
||||
@@ -67,8 +68,8 @@ var _ = Describe("Sync", func() {
|
||||
},
|
||||
status: &model.ServerStatus{},
|
||||
safeSearch: &model.SafeSearchConfig{},
|
||||
queryLogConfig: &model.QueryLogConfig{},
|
||||
statsConfig: &model.StatsConfig{},
|
||||
queryLogConfig: &model.QueryLogConfigWithIgnored{},
|
||||
statsConfig: &model.PutStatsConfigUpdateRequest{},
|
||||
},
|
||||
replicaStatus: &model.ServerStatus{},
|
||||
client: cl,
|
||||
@@ -258,16 +259,32 @@ var _ = Describe("Sync", func() {
|
||||
err := actionProfileInfo(ac)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
It("should not change theme if feature is disabled", func() {
|
||||
ac.origin.profileInfo.Language = "de"
|
||||
ac.cfg.Features.Theme = false
|
||||
cl.EXPECT().ProfileInfo().Return(&model.ProfileInfo{Name: "replica", Language: "en"}, nil)
|
||||
cl.EXPECT().SetProfileInfo(&model.ProfileInfo{
|
||||
Language: "de",
|
||||
Name: "replica",
|
||||
Theme: "",
|
||||
})
|
||||
err := actionProfileInfo(ac)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
It("should not sync profileInfo if language is not set", func() {
|
||||
ac.origin.profileInfo.Language = ""
|
||||
cl.EXPECT().ProfileInfo().Return(&model.ProfileInfo{Name: "replica", Language: "en", Theme: "auto"}, nil)
|
||||
cl.EXPECT().
|
||||
ProfileInfo().
|
||||
Return(&model.ProfileInfo{Name: "replica", Language: "en", Theme: "auto"}, nil)
|
||||
cl.EXPECT().SetProfileInfo(ac.origin.profileInfo).Times(0)
|
||||
err := actionProfileInfo(ac)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
It("should not sync profileInfo if theme is not set", func() {
|
||||
ac.origin.profileInfo.Theme = ""
|
||||
cl.EXPECT().ProfileInfo().Return(&model.ProfileInfo{Name: "replica", Language: "en", Theme: "auto"}, nil)
|
||||
cl.EXPECT().
|
||||
ProfileInfo().
|
||||
Return(&model.ProfileInfo{Name: "replica", Language: "en", Theme: "auto"}, nil)
|
||||
cl.EXPECT().SetProfileInfo(ac.origin.profileInfo).Times(0)
|
||||
err := actionProfileInfo(ac)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
@@ -289,9 +306,9 @@ var _ = Describe("Sync", func() {
|
||||
})
|
||||
})
|
||||
Context("actionQueryLogConfig", func() {
|
||||
var qlc *model.QueryLogConfig
|
||||
var qlc *model.QueryLogConfigWithIgnored
|
||||
BeforeEach(func() {
|
||||
qlc = &model.QueryLogConfig{}
|
||||
qlc = &model.QueryLogConfigWithIgnored{}
|
||||
})
|
||||
It("should have no changes", func() {
|
||||
cl.EXPECT().QueryLogConfig().Return(qlc, nil)
|
||||
@@ -302,15 +319,16 @@ var _ = Describe("Sync", func() {
|
||||
var interval model.QueryLogConfigInterval = 123
|
||||
ac.origin.queryLogConfig.Interval = &interval
|
||||
cl.EXPECT().QueryLogConfig().Return(qlc, nil)
|
||||
cl.EXPECT().SetQueryLogConfig(&model.QueryLogConfig{AnonymizeClientIp: nil, Interval: &interval, Enabled: nil})
|
||||
cl.EXPECT().
|
||||
SetQueryLogConfig(&model.QueryLogConfigWithIgnored{QueryLogConfig: model.QueryLogConfig{AnonymizeClientIp: nil, Interval: &interval, Enabled: nil}})
|
||||
err := actionQueryLogConfig(ac)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
Context("syncConfigs", func() {
|
||||
var sc *model.StatsConfig
|
||||
var sc *model.PutStatsConfigUpdateRequest
|
||||
BeforeEach(func() {
|
||||
sc = &model.StatsConfig{}
|
||||
sc = &model.PutStatsConfigUpdateRequest{}
|
||||
})
|
||||
It("should have no changes", func() {
|
||||
cl.EXPECT().StatsConfig().Return(sc, nil)
|
||||
@@ -318,10 +336,10 @@ var _ = Describe("Sync", func() {
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
It("should have StatsConfig changes", func() {
|
||||
var interval model.StatsConfigInterval = 123
|
||||
ac.origin.statsConfig.Interval = &interval
|
||||
var interval float32 = 123
|
||||
ac.origin.statsConfig.Interval = interval
|
||||
cl.EXPECT().StatsConfig().Return(sc, nil)
|
||||
cl.EXPECT().SetStatsConfig(&model.StatsConfig{Interval: &interval})
|
||||
cl.EXPECT().SetStatsConfig(&model.PutStatsConfigUpdateRequest{Interval: interval})
|
||||
err := actionStatsConfig(ac)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
@@ -359,26 +377,6 @@ var _ = Describe("Sync", func() {
|
||||
Ω(st).Should(BeNil())
|
||||
})
|
||||
})
|
||||
Context("actionBlockedServices", func() {
|
||||
var rbs *model.BlockedServicesArray
|
||||
BeforeEach(func() {
|
||||
ac.origin.blockedServices = &model.BlockedServicesArray{"foo"}
|
||||
rbs = &model.BlockedServicesArray{"foo"}
|
||||
})
|
||||
It("should have no changes", func() {
|
||||
cl.EXPECT().BlockedServices().Return(rbs, nil)
|
||||
err := actionBlockedServices(ac)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
It("should have blockedServices changes", func() {
|
||||
ac.origin.blockedServices = &model.BlockedServicesArray{"bar"}
|
||||
|
||||
cl.EXPECT().BlockedServices().Return(rbs, nil)
|
||||
cl.EXPECT().SetBlockedServices(ac.origin.blockedServices)
|
||||
err := actionBlockedServices(ac)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
Context("actionBlockedServicesSchedule", func() {
|
||||
var rbss *model.BlockedServicesSchedule
|
||||
BeforeEach(func() {
|
||||
@@ -441,7 +439,9 @@ var _ = Describe("Sync", func() {
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
It("should update a filter", func() {
|
||||
ac.origin.filters.Filters = utils.Ptr([]model.Filter{{Name: "foo", Url: "https://foo.bar", Enabled: true}})
|
||||
ac.origin.filters.Filters = utils.Ptr(
|
||||
[]model.Filter{{Name: "foo", Url: "https://foo.bar", Enabled: true}},
|
||||
)
|
||||
rf.Filters = utils.Ptr([]model.Filter{{Name: "foo", Url: "https://foo.bar"}})
|
||||
cl.EXPECT().Filtering().Return(rf, nil)
|
||||
cl.EXPECT().UpdateFilter(false, model.Filter{Name: "foo", Url: "https://foo.bar", Enabled: true})
|
||||
@@ -451,19 +451,25 @@ var _ = Describe("Sync", func() {
|
||||
})
|
||||
|
||||
It("should abort after failed added filter", func() {
|
||||
ac.continueOnError = false
|
||||
ac.cfg.ContinueOnError = false
|
||||
ac.origin.filters.Filters = utils.Ptr([]model.Filter{{Name: "foo", Url: "https://foo.bar"}})
|
||||
cl.EXPECT().Filtering().Return(rf, nil)
|
||||
cl.EXPECT().AddFilter(false, model.Filter{Name: "foo", Url: "https://foo.bar"}).Return(errors.New("test failure"))
|
||||
cl.EXPECT().
|
||||
AddFilter(false, model.Filter{Name: "foo", Url: "https://foo.bar"}).
|
||||
Return(errors.New("test failure"))
|
||||
err := actionFilters(ac)
|
||||
Ω(err).Should(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should continue after failed added filter", func() {
|
||||
ac.continueOnError = true
|
||||
ac.origin.filters.Filters = utils.Ptr([]model.Filter{{Name: "foo", Url: "https://foo.bar"}, {Name: "bar", Url: "https://bar.foo"}})
|
||||
ac.cfg.ContinueOnError = true
|
||||
ac.origin.filters.Filters = utils.Ptr(
|
||||
[]model.Filter{{Name: "foo", Url: "https://foo.bar"}, {Name: "bar", Url: "https://bar.foo"}},
|
||||
)
|
||||
cl.EXPECT().Filtering().Return(rf, nil)
|
||||
cl.EXPECT().AddFilter(false, model.Filter{Name: "foo", Url: "https://foo.bar"}).Return(errors.New("test failure"))
|
||||
cl.EXPECT().
|
||||
AddFilter(false, model.Filter{Name: "foo", Url: "https://foo.bar"}).
|
||||
Return(errors.New("test failure"))
|
||||
cl.EXPECT().AddFilter(false, model.Filter{Name: "bar", Url: "https://bar.foo"})
|
||||
cl.EXPECT().RefreshFilters(gm.Any())
|
||||
err := actionFilters(ac)
|
||||
@@ -599,12 +605,11 @@ var _ = Describe("Sync", func() {
|
||||
cl.EXPECT().SafeSearchConfig().Return(&model.SafeSearchConfig{}, nil)
|
||||
cl.EXPECT().SafeBrowsing()
|
||||
cl.EXPECT().RewriteList().Return(&model.RewriteEntries{}, nil)
|
||||
cl.EXPECT().BlockedServices()
|
||||
cl.EXPECT().BlockedServicesSchedule()
|
||||
cl.EXPECT().Filtering().Return(&model.FilterStatus{}, nil)
|
||||
cl.EXPECT().Clients().Return(&model.Clients{}, nil)
|
||||
cl.EXPECT().QueryLogConfig().Return(&model.QueryLogConfig{}, nil)
|
||||
cl.EXPECT().StatsConfig().Return(&model.StatsConfig{}, nil)
|
||||
cl.EXPECT().QueryLogConfig().Return(&model.QueryLogConfigWithIgnored{}, nil)
|
||||
cl.EXPECT().StatsConfig().Return(&model.PutStatsConfigUpdateRequest{}, nil)
|
||||
cl.EXPECT().AccessList().Return(&model.AccessList{}, nil)
|
||||
cl.EXPECT().DNSConfig().Return(&model.DNSConfig{}, nil)
|
||||
cl.EXPECT().DhcpConfig().Return(&model.DhcpStatus{}, nil)
|
||||
@@ -616,13 +621,12 @@ var _ = Describe("Sync", func() {
|
||||
cl.EXPECT().Parental()
|
||||
cl.EXPECT().SafeSearchConfig().Return(&model.SafeSearchConfig{}, nil)
|
||||
cl.EXPECT().SafeBrowsing()
|
||||
cl.EXPECT().QueryLogConfig().Return(&model.QueryLogConfig{}, nil)
|
||||
cl.EXPECT().StatsConfig().Return(&model.StatsConfig{}, nil)
|
||||
cl.EXPECT().QueryLogConfig().Return(&model.QueryLogConfigWithIgnored{}, nil)
|
||||
cl.EXPECT().StatsConfig().Return(&model.PutStatsConfigUpdateRequest{}, nil)
|
||||
cl.EXPECT().RewriteList().Return(&model.RewriteEntries{}, nil)
|
||||
cl.EXPECT().AddRewriteEntries()
|
||||
cl.EXPECT().DeleteRewriteEntries()
|
||||
cl.EXPECT().Filtering().Return(&model.FilterStatus{}, nil)
|
||||
cl.EXPECT().BlockedServices()
|
||||
cl.EXPECT().BlockedServicesSchedule()
|
||||
cl.EXPECT().Clients().Return(&model.Clients{}, nil)
|
||||
cl.EXPECT().AccessList().Return(&model.AccessList{}, nil)
|
||||
@@ -641,12 +645,11 @@ var _ = Describe("Sync", func() {
|
||||
cl.EXPECT().SafeSearchConfig().Return(&model.SafeSearchConfig{}, nil)
|
||||
cl.EXPECT().SafeBrowsing()
|
||||
cl.EXPECT().RewriteList().Return(&model.RewriteEntries{}, nil)
|
||||
cl.EXPECT().BlockedServices()
|
||||
cl.EXPECT().BlockedServicesSchedule()
|
||||
cl.EXPECT().Filtering().Return(&model.FilterStatus{}, nil)
|
||||
cl.EXPECT().Clients().Return(&model.Clients{}, nil)
|
||||
cl.EXPECT().QueryLogConfig().Return(&model.QueryLogConfig{}, nil)
|
||||
cl.EXPECT().StatsConfig().Return(&model.StatsConfig{}, nil)
|
||||
cl.EXPECT().QueryLogConfig().Return(&model.QueryLogConfigWithIgnored{}, nil)
|
||||
cl.EXPECT().StatsConfig().Return(&model.PutStatsConfigUpdateRequest{}, nil)
|
||||
cl.EXPECT().AccessList().Return(&model.AccessList{}, nil)
|
||||
cl.EXPECT().DNSConfig().Return(&model.DNSConfig{}, nil)
|
||||
|
||||
@@ -657,13 +660,12 @@ var _ = Describe("Sync", func() {
|
||||
cl.EXPECT().Parental()
|
||||
cl.EXPECT().SafeSearchConfig().Return(&model.SafeSearchConfig{}, nil)
|
||||
cl.EXPECT().SafeBrowsing()
|
||||
cl.EXPECT().QueryLogConfig().Return(&model.QueryLogConfig{}, nil)
|
||||
cl.EXPECT().StatsConfig().Return(&model.StatsConfig{}, nil)
|
||||
cl.EXPECT().QueryLogConfig().Return(&model.QueryLogConfigWithIgnored{}, nil)
|
||||
cl.EXPECT().StatsConfig().Return(&model.PutStatsConfigUpdateRequest{}, nil)
|
||||
cl.EXPECT().RewriteList().Return(&model.RewriteEntries{}, nil)
|
||||
cl.EXPECT().AddRewriteEntries()
|
||||
cl.EXPECT().DeleteRewriteEntries()
|
||||
cl.EXPECT().Filtering().Return(&model.FilterStatus{}, nil)
|
||||
cl.EXPECT().BlockedServices()
|
||||
cl.EXPECT().BlockedServicesSchedule()
|
||||
cl.EXPECT().Clients().Return(&model.Clients{}, nil)
|
||||
cl.EXPECT().AccessList().Return(&model.AccessList{}, nil)
|
||||
@@ -685,12 +687,11 @@ var _ = Describe("Sync", func() {
|
||||
cl.EXPECT().SafeSearchConfig().Return(&model.SafeSearchConfig{}, nil)
|
||||
cl.EXPECT().SafeBrowsing()
|
||||
cl.EXPECT().RewriteList().Return(&model.RewriteEntries{}, nil)
|
||||
cl.EXPECT().BlockedServices()
|
||||
cl.EXPECT().BlockedServicesSchedule()
|
||||
cl.EXPECT().Filtering().Return(&model.FilterStatus{}, nil)
|
||||
cl.EXPECT().Clients().Return(&model.Clients{}, nil)
|
||||
cl.EXPECT().QueryLogConfig().Return(&model.QueryLogConfig{}, nil)
|
||||
cl.EXPECT().StatsConfig().Return(&model.StatsConfig{}, nil)
|
||||
cl.EXPECT().QueryLogConfig().Return(&model.QueryLogConfigWithIgnored{}, nil)
|
||||
cl.EXPECT().StatsConfig().Return(&model.PutStatsConfigUpdateRequest{}, nil)
|
||||
cl.EXPECT().AccessList().Return(&model.AccessList{}, nil)
|
||||
cl.EXPECT().DNSConfig().Return(&model.DNSConfig{}, nil)
|
||||
cl.EXPECT().DhcpConfig().Return(&model.DhcpStatus{}, nil)
|
||||
|
||||
@@ -21,19 +21,21 @@ func NewFeatures(enabled bool) Features {
|
||||
ClientSettings: enabled,
|
||||
Services: enabled,
|
||||
Filters: enabled,
|
||||
Theme: enabled,
|
||||
}
|
||||
}
|
||||
|
||||
// Features feature flags
|
||||
type Features struct {
|
||||
DNS DNS `json:"dns" yaml:"dns"`
|
||||
DHCP DHCP `json:"dhcp" yaml:"dhcp"`
|
||||
DNS DNS `json:"dns" yaml:"dns"`
|
||||
DHCP DHCP `json:"dhcp" yaml:"dhcp"`
|
||||
GeneralSettings bool `json:"generalSettings" yaml:"generalSettings" env:"FEATURES_GENERAL_SETTINGS"`
|
||||
QueryLogConfig bool `json:"queryLogConfig" yaml:"queryLogConfig" env:"FEATURES_QUERY_LOG_CONFIG"`
|
||||
StatsConfig bool `json:"statsConfig" yaml:"statsConfig" env:"FEATURES_STATS_CONFIG"`
|
||||
ClientSettings bool `json:"clientSettings" yaml:"clientSettings" env:"FEATURES_CLIENT_SETTINGS"`
|
||||
Services bool `json:"services" yaml:"services" env:"FEATURES_SERVICES"`
|
||||
Filters bool `json:"filters" yaml:"filters" env:"FEATURES_FILTERS"`
|
||||
QueryLogConfig bool `json:"queryLogConfig" yaml:"queryLogConfig" env:"FEATURES_QUERY_LOG_CONFIG"`
|
||||
StatsConfig bool `json:"statsConfig" yaml:"statsConfig" env:"FEATURES_STATS_CONFIG"`
|
||||
ClientSettings bool `json:"clientSettings" yaml:"clientSettings" env:"FEATURES_CLIENT_SETTINGS"`
|
||||
Services bool `json:"services" yaml:"services" env:"FEATURES_SERVICES"`
|
||||
Filters bool `json:"filters" yaml:"filters" env:"FEATURES_FILTERS"`
|
||||
Theme bool `json:"theme" yaml:"theme" env:"FEATURES_THEME"`
|
||||
}
|
||||
|
||||
// DHCP features
|
||||
@@ -44,9 +46,9 @@ type DHCP struct {
|
||||
|
||||
// DNS features
|
||||
type DNS struct {
|
||||
AccessLists bool `json:"accessLists" yaml:"accessLists" env:"FEATURES_DNS_ACCESS_LISTS"`
|
||||
AccessLists bool `json:"accessLists" yaml:"accessLists" env:"FEATURES_DNS_ACCESS_LISTS"`
|
||||
ServerConfig bool `json:"serverConfig" yaml:"serverConfig" env:"FEATURES_DNS_SERVER_CONFIG"`
|
||||
Rewrites bool `json:"rewrites" yaml:"rewrites" env:"FEATURES_DNS_REWRITES"`
|
||||
Rewrites bool `json:"rewrites" yaml:"rewrites" env:"FEATURES_DNS_REWRITES"`
|
||||
}
|
||||
|
||||
// LogDisabled log all disabled features
|
||||
|
||||
@@ -3,7 +3,9 @@ package types
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -16,23 +18,56 @@ const (
|
||||
// Config application configuration struct
|
||||
// +k8s:deepcopy-gen=true
|
||||
type Config struct {
|
||||
Origin AdGuardInstance `json:"origin" yaml:"origin" env:"ORIGIN"`
|
||||
Replica *AdGuardInstance `json:"replica,omitempty" yaml:"replica,omitempty" env:"REPLICA"`
|
||||
Replicas []AdGuardInstance `json:"replicas,omitempty" yaml:"replicas,omitempty"`
|
||||
Cron string `json:"cron,omitempty" yaml:"cron,omitempty" env:"CRON"`
|
||||
RunOnStart bool `json:"runOnStart,omitempty" yaml:"runOnStart,omitempty" env:"RUN_ON_START"`
|
||||
Origin AdGuardInstance `json:"origin" yaml:"origin" env:"ORIGIN"`
|
||||
Replica *AdGuardInstance `json:"replica,omitempty" yaml:"replica,omitempty" env:"REPLICA"`
|
||||
Replicas []AdGuardInstance `json:"replicas,omitempty" yaml:"replicas,omitempty" faker:"slice_len=2"`
|
||||
Cron string `json:"cron,omitempty" yaml:"cron,omitempty" env:"CRON"`
|
||||
RunOnStart bool `json:"runOnStart,omitempty" yaml:"runOnStart,omitempty" env:"RUN_ON_START"`
|
||||
PrintConfigOnly bool `json:"printConfigOnly,omitempty" yaml:"printConfigOnly,omitempty" env:"PRINT_CONFIG_ONLY"`
|
||||
ContinueOnError bool `json:"continueOnError,omitempty" yaml:"continueOnError,omitempty" env:"CONTINUE_ON_ERROR"`
|
||||
API API `json:"api,omitempty" yaml:"api,omitempty" env:"API"`
|
||||
Features Features `json:"features,omitempty" yaml:"features,omitempty" env:"FEATURES_"`
|
||||
API API `json:"api,omitempty" yaml:"api,omitempty" env:"API"`
|
||||
Features Features `json:"features,omitempty" yaml:"features,omitempty" env:"FEATURES_"`
|
||||
}
|
||||
|
||||
// API configuration
|
||||
type API struct {
|
||||
Port int `json:"port,omitempty" yaml:"port,omitempty" env:"API_PORT"`
|
||||
Username string `json:"username,omitempty" yaml:"username,omitempty" env:"API_USERNAME"`
|
||||
Password string `json:"password,omitempty" yaml:"password,omitempty" env:"API_PASSWORD"`
|
||||
DarkMode bool `json:"darkMode,omitempty" yaml:"darkMode,omitempty" env:"API_DARK_MODE"`
|
||||
Port int `json:"port,omitempty" yaml:"port,omitempty" env:"API_PORT"`
|
||||
Username string `json:"username,omitempty" yaml:"username,omitempty" env:"API_USERNAME"`
|
||||
Password string `json:"password,omitempty" yaml:"password,omitempty" env:"API_PASSWORD"`
|
||||
DarkMode bool `json:"darkMode,omitempty" yaml:"darkMode,omitempty" env:"API_DARK_MODE"`
|
||||
Metrics Metrics `json:"metrics,omitempty" yaml:"metrics,omitempty" env:"API_METRICS"`
|
||||
TLS TLS `json:"tls,omitempty" yaml:"tls,omitempty" env:"API_TLS"`
|
||||
}
|
||||
|
||||
// Metrics configuration
|
||||
type Metrics struct {
|
||||
Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty" env:"API_METRICS_ENABLED"`
|
||||
ScrapeInterval time.Duration `json:"scrapeInterval,omitempty" yaml:"scrapeInterval,omitempty" env:"API_METRICS_SCRAPE_INTERVAL"`
|
||||
QueryLogLimit int `json:"queryLogLimit,omitempty" yaml:"queryLogLimit,omitempty" env:"API_METRICS_QUERY_LOG_LIMIT"`
|
||||
}
|
||||
|
||||
// TLS configuration
|
||||
type TLS struct {
|
||||
CertDir string `json:"certDir,omitempty" yaml:"certDir,omitempty" env:"API_TLS_CERT_DIR"`
|
||||
CertName string `json:"certName,omitempty" yaml:"certName,omitempty" env:"API_TLS_CERT_NAME"`
|
||||
KeyName string `json:"keyName,omitempty" yaml:"keyName,omitempty" env:"API_TLS_KEY_NAME"`
|
||||
}
|
||||
|
||||
func (t TLS) Enabled() bool {
|
||||
return strings.TrimSpace(t.CertDir) != ""
|
||||
}
|
||||
|
||||
func (t TLS) Certs() (cert string, key string) {
|
||||
cert = filepath.Join(t.CertDir, defaultIfEmpty(t.CertName, "tls.crt"))
|
||||
key = filepath.Join(t.CertDir, defaultIfEmpty(t.KeyName, "tls.key"))
|
||||
return
|
||||
}
|
||||
|
||||
func defaultIfEmpty(val string, fallback string) string {
|
||||
if strings.TrimSpace(val) == "" {
|
||||
return fallback
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// Mask maks username and password
|
||||
@@ -105,15 +140,15 @@ func (cfg *Config) Init() error {
|
||||
// AdGuardInstance AdguardHome config instance
|
||||
// +k8s:deepcopy-gen=true
|
||||
type AdGuardInstance struct {
|
||||
URL string `json:"url" yaml:"url" env:"URL"`
|
||||
WebURL string `json:"webURL" yaml:"webURL" env:"WEB_URL"`
|
||||
APIPath string `json:"apiPath,omitempty" yaml:"apiPath,omitempty" env:"API_PATH"`
|
||||
Username string `json:"username,omitempty" yaml:"username,omitempty" env:"USERNAME"`
|
||||
Password string `json:"password,omitempty" yaml:"password,omitempty" env:"PASSWORD"`
|
||||
Cookie string `json:"cookie,omitempty" yaml:"cookie,omitempty" env:"COOKIE"`
|
||||
InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify" env:"INSECURE_SKIP_VERIFY"`
|
||||
AutoSetup bool `json:"autoSetup" yaml:"autoSetup" env:"AUTO_SETUP"`
|
||||
InterfaceName string `json:"interfaceName,omitempty" yaml:"interfaceName,omitempty" env:"INTERFACE_NAME"`
|
||||
URL string `json:"url" yaml:"url" env:"URL" faker:"url"`
|
||||
WebURL string `json:"webURL" yaml:"webURL" env:"WEB_URL" faker:"url"`
|
||||
APIPath string `json:"apiPath,omitempty" yaml:"apiPath,omitempty" env:"API_PATH"`
|
||||
Username string `json:"username,omitempty" yaml:"username,omitempty" env:"USERNAME"`
|
||||
Password string `json:"password,omitempty" yaml:"password,omitempty" env:"PASSWORD"`
|
||||
Cookie string `json:"cookie,omitempty" yaml:"cookie,omitempty" env:"COOKIE"`
|
||||
InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify" env:"INSECURE_SKIP_VERIFY"`
|
||||
AutoSetup bool `json:"autoSetup" yaml:"autoSetup" env:"AUTO_SETUP"`
|
||||
InterfaceName string `json:"interfaceName,omitempty" yaml:"interfaceName,omitempty" env:"INTERFACE_NAME"`
|
||||
DHCPServerEnabled *bool `json:"dhcpServerEnabled,omitempty" yaml:"dhcpServerEnabled,omitempty" env:"DHCP_SERVER_ENABLED"`
|
||||
|
||||
Host string `json:"-" yaml:"-"`
|
||||
|
||||
@@ -96,4 +96,35 @@ var _ = Describe("Types", func() {
|
||||
})
|
||||
})
|
||||
})
|
||||
Context("TLS", func() {
|
||||
var t TLS
|
||||
BeforeEach(func() {
|
||||
t = TLS{
|
||||
CertDir: "/path/to/certs",
|
||||
}
|
||||
})
|
||||
Context("Enabled", func() {
|
||||
It("should use enabled", func() {
|
||||
Ω(t.Enabled()).Should(BeTrue())
|
||||
})
|
||||
It("should use disabled", func() {
|
||||
t.CertDir = " "
|
||||
Ω(t.Enabled()).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
Context("Certs", func() {
|
||||
It("should use default crt and key", func() {
|
||||
crt, key := t.Certs()
|
||||
Ω(crt).Should(Equal("/path/to/certs/tls.crt"))
|
||||
Ω(key).Should(Equal("/path/to/certs/tls.key"))
|
||||
})
|
||||
It("should use custom crt and key", func() {
|
||||
t.CertName = "foo.crt"
|
||||
t.KeyName = "bar.key"
|
||||
crt, key := t.Certs()
|
||||
Ω(crt).Should(Equal("/path/to/certs/foo.crt"))
|
||||
Ω(key).Should(Equal("/path/to/certs/bar.key"))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,3 +12,13 @@ func PtrToString[I interface{}](i *I) string {
|
||||
}
|
||||
return fmt.Sprintf("%v", i)
|
||||
}
|
||||
|
||||
func PtrEquals[I comparable](a *I, b *I) bool {
|
||||
if a == nil && b == nil {
|
||||
return true
|
||||
}
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
return *a == *b
|
||||
}
|
||||
|
||||
28
renovate.json
Normal file
28
renovate.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"customManagers": [
|
||||
{
|
||||
"customType": "regex",
|
||||
"datasourceTemplate": "go",
|
||||
"description": "Update toolbox tools in .toolbox.mk",
|
||||
"fileMatch": [
|
||||
"^\\.toolbox\\.mk$"
|
||||
],
|
||||
"matchStrings": [
|
||||
"# renovate: packageName=(?<packageName>.+?)\\s+.+?_VERSION \\?= (?<currentValue>.+?)\\s"
|
||||
]
|
||||
},
|
||||
{
|
||||
"customType": "regex",
|
||||
"datasourceTemplate": "github-releases",
|
||||
"description": "Update github _VERSION Makefile",
|
||||
"fileMatch": [
|
||||
"^Makefile$"
|
||||
],
|
||||
"matchStrings": [
|
||||
"# renovate: packageName=(?<packageName>.+?)\\s+.+?_VERSION \\?= (?<currentValue>.+?)\\s"
|
||||
]
|
||||
}
|
||||
],
|
||||
"dependencyDashboard": true
|
||||
}
|
||||
2
testdata/config_test_replica.yaml
vendored
2
testdata/config_test_replica.yaml
vendored
@@ -15,6 +15,7 @@ replica:
|
||||
insecureSkipVerify: false
|
||||
autoSetup: false
|
||||
interfaceName: eth3
|
||||
dhcpServerEnabled: false
|
||||
cron: '*/15 * * * *'
|
||||
runOnStart: true
|
||||
printConfigOnly: true
|
||||
@@ -34,3 +35,4 @@ features:
|
||||
clientSettings: true
|
||||
services: true
|
||||
filters: true
|
||||
theme: true
|
||||
|
||||
2
testdata/config_test_replicas.yaml
vendored
2
testdata/config_test_replicas.yaml
vendored
@@ -15,6 +15,7 @@ replicas:
|
||||
insecureSkipVerify: false
|
||||
autoSetup: false
|
||||
interfaceName: eth3
|
||||
dhcpServerEnabled: false
|
||||
cron: '*/15 * * * *'
|
||||
runOnStart: true
|
||||
printConfigOnly: true
|
||||
@@ -34,3 +35,4 @@ features:
|
||||
clientSettings: true
|
||||
services: true
|
||||
filters: true
|
||||
theme: true
|
||||
|
||||
@@ -15,6 +15,7 @@ replica:
|
||||
insecureSkipVerify: false
|
||||
autoSetup: false
|
||||
interfaceName: eth3
|
||||
dhcpServerEnabled: false
|
||||
replicas:
|
||||
- url: https://replicas-file:443
|
||||
webURL: https://replicas-file:443
|
||||
@@ -24,6 +25,7 @@ replicas:
|
||||
insecureSkipVerify: false
|
||||
autoSetup: false
|
||||
interfaceName: eth3
|
||||
dhcpServerEnabled: false
|
||||
cron: '*/15 * * * *'
|
||||
runOnStart: true
|
||||
printConfigOnly: true
|
||||
@@ -43,3 +45,4 @@ features:
|
||||
clientSettings: true
|
||||
services: true
|
||||
filters: true
|
||||
theme: true
|
||||
|
||||
1
testdata/e2e/bin/install-chart.sh
vendored
1
testdata/e2e/bin/install-chart.sh
vendored
@@ -7,3 +7,4 @@ if [[ $(helm list --no-headers -n agh-e2e | grep agh-e2e | wc -l) == "1" ]]; the
|
||||
helm delete agh-e2e -n agh-e2e --wait
|
||||
fi
|
||||
helm install agh-e2e testdata/e2e -n agh-e2e --create-namespace --set mode=${1}
|
||||
|
||||
|
||||
2
testdata/e2e/bin/show-origin-logs.sh
vendored
2
testdata/e2e/bin/show-origin-logs.sh
vendored
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "## Pod adguardhome-origin logs" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## Pod adguardhome-origin ${1} logs" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
kubectl logs adguardhome-origin >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
21
testdata/e2e/bin/show-replica-logs.sh
vendored
21
testdata/e2e/bin/show-replica-logs.sh
vendored
@@ -4,15 +4,18 @@ set -e
|
||||
for pod in $(kubectl get pods -l bakito.net/adguardhome-sync=replica -o name); do
|
||||
echo "## Pod ${pod} logs" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
LOGS=$(kubectl logs ${pod})
|
||||
K8S_LOGS=$(kubectl logs ${pod})
|
||||
# ignore certain errors
|
||||
LOGS=$(echo -e "${LOGS}" | grep -v -e "error.* deleting filter .* no such file or directory" )
|
||||
# https://github.com/AdguardTeam/AdGuardHome/issues/4944
|
||||
LOGS=$(echo -e "${LOGS}" | grep -v -e "error.* creating dhcpv4 srv")
|
||||
echo -e "${LOGS}" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$(echo -e "${LOGS}"} | grep '\[error\]' | wc -l)
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "Found ${ERRORS} error(s) in ${pod} log" >> $GITHUB_STEP_SUMMARY
|
||||
echo "----------------------------------------------" >> $GITHUB_STEP_SUMMARY
|
||||
LOGS=$(echo -e "${K8S_LOGS}" |
|
||||
grep -v -e "error.* deleting filter .* no such file or directory" |
|
||||
grep -v -e '\[error\] storage: recovered from panic: runtime' # https://github.com/AdguardTeam/AdGuardHome/issues/7315
|
||||
)
|
||||
|
||||
echo -e "${K8S_LOGS}" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$(echo -e "${LOGS}"} | grep '\[error\]' | wc -l)
|
||||
TOTAL_ERRORS=$(echo -e "${K8S_LOGS}"} | grep '\[error\]' | wc -l)
|
||||
IGNORED_ERRORS=$(echo "${TOTAL_ERRORS} - ${ERRORS}" | bc)
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
echo "Found ${ERRORS} error(s) (${IGNORED_ERRORS} ignored) in ${pod} log" >> $GITHUB_STEP_SUMMARY
|
||||
echo "----------------------------------------------" >> $GITHUB_STEP_SUMMARY
|
||||
done
|
||||
|
||||
11
testdata/e2e/bin/show-sync-metrics.sh
vendored
Executable file
11
testdata/e2e/bin/show-sync-metrics.sh
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "wait another scrape interval (30s)"
|
||||
|
||||
sleep 30
|
||||
|
||||
echo "## Pod adguardhome-sync metrics" >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
curl ${1}://localhost:9090/metrics -s -k >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
3
testdata/e2e/bin/wait-for-start.sh
vendored
Executable file
3
testdata/e2e/bin/wait-for-start.sh
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
kubectl wait --for=jsonpath='{.status.phase}'=Running pod/adguardhome-sync --timeout=1m
|
||||
16
testdata/e2e/bin/wait-for-sync.sh
vendored
16
testdata/e2e/bin/wait-for-sync.sh
vendored
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
kubectl wait --for=jsonpath='{.status.phase}'=Succeeded pod/adguardhome-sync --timeout=1m
|
||||
RESULT=$?
|
||||
if [[ "${RESULT}" != "0" ]]; then
|
||||
kubectl logs adguardhome-sync
|
||||
fi
|
||||
exit ${RESULT}
|
||||
kubectl port-forward pod/adguardhome-sync 9090:9090 &
|
||||
|
||||
for i in {1..6}; do
|
||||
sleep 10
|
||||
RUNNING=$(curl ${1}://localhost:9090/api/v1/status -s -k | jq -r .syncRunning)
|
||||
echo "SyncRunning = ${RUNNING}"
|
||||
if [[ "${RUNNING}" == "false" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
44
testdata/e2e/resources/AdGuardHome.yaml
vendored
44
testdata/e2e/resources/AdGuardHome.yaml
vendored
@@ -18,19 +18,18 @@ dns:
|
||||
port: 53
|
||||
anonymize_client_ip: false
|
||||
ratelimit: 20
|
||||
ratelimit_whitelist: [ ]
|
||||
ratelimit_whitelist: []
|
||||
refuse_any: true
|
||||
upstream_dns:
|
||||
- https://dns10.quad9.net/dns-query
|
||||
- 8.8.8.8
|
||||
upstream_dns_file: ""
|
||||
bootstrap_dns:
|
||||
- 1.1.1.1:53
|
||||
fallback_dns: [ ]
|
||||
all_servers: false
|
||||
fastest_addr: false
|
||||
fallback_dns: []
|
||||
upstream_mode: load_balance
|
||||
fastest_timeout: 1s
|
||||
allowed_clients: [ ]
|
||||
disallowed_clients: [ ]
|
||||
allowed_clients: []
|
||||
disallowed_clients: []
|
||||
blocked_hosts:
|
||||
- version.bind
|
||||
- id.server
|
||||
@@ -42,7 +41,7 @@ dns:
|
||||
cache_ttl_min: 0
|
||||
cache_ttl_max: 0
|
||||
cache_optimistic: true
|
||||
bogus_nxdomain: [ ]
|
||||
bogus_nxdomain: []
|
||||
aaaa_disabled: false
|
||||
enable_dnssec: false
|
||||
edns_client_subnet:
|
||||
@@ -51,17 +50,19 @@ dns:
|
||||
use_custom: false
|
||||
max_goroutines: 300
|
||||
handle_ddr: true
|
||||
ipset: [ ]
|
||||
ipset: []
|
||||
ipset_file: ""
|
||||
bootstrap_prefer_ipv6: false
|
||||
upstream_timeout: 10s
|
||||
private_networks: [ ]
|
||||
private_networks: []
|
||||
use_private_ptr_resolvers: true
|
||||
local_ptr_upstreams: [ ]
|
||||
local_ptr_upstreams: []
|
||||
use_dns64: false
|
||||
dns64_prefixes: [ ]
|
||||
dns64_prefixes: []
|
||||
serve_http3: false
|
||||
use_http3_upstreams: false
|
||||
serve_plain_dns: true
|
||||
hostsfile_enabled: true
|
||||
tls:
|
||||
enabled: false
|
||||
server_name: ""
|
||||
@@ -78,13 +79,15 @@ tls:
|
||||
private_key_path: ""
|
||||
strict_sni_check: false
|
||||
querylog:
|
||||
ignored: [ ]
|
||||
dir_path: ""
|
||||
ignored: []
|
||||
interval: 6h
|
||||
size_memory: 1000
|
||||
enabled: true
|
||||
file_enabled: true
|
||||
statistics:
|
||||
ignored: [ ]
|
||||
dir_path: ""
|
||||
ignored: []
|
||||
interval: 24h
|
||||
enabled: true
|
||||
filters:
|
||||
@@ -96,7 +99,7 @@ filters:
|
||||
url: https://adaway.org/hosts.txt
|
||||
name: AdAway Default Blocklist
|
||||
id: 2
|
||||
whitelist_filters: [ ]
|
||||
whitelist_filters: []
|
||||
user_rules:
|
||||
- '||metrics2.data.hicloud.com^$important'
|
||||
- '||www.curiouscorrespondence.com^$important'
|
||||
@@ -113,7 +116,7 @@ dhcp:
|
||||
range_end: 1.2.3.56
|
||||
lease_duration: 86400
|
||||
icmp_timeout_msec: 1000
|
||||
options: [ ]
|
||||
options: []
|
||||
dhcpv6:
|
||||
range_start: ""
|
||||
lease_duration: 86400
|
||||
@@ -150,7 +153,7 @@ filtering:
|
||||
blocking_mode: default
|
||||
parental_block_host: family-block.dns.adguard.com
|
||||
safebrowsing_block_host: standard-block.dns.adguard.com
|
||||
rewrites: [ ]
|
||||
rewrites: []
|
||||
safebrowsing_cache_size: 1048576
|
||||
safesearch_cache_size: 1048576
|
||||
parental_cache_size: 1048576
|
||||
@@ -170,8 +173,6 @@ clients:
|
||||
hosts: true
|
||||
persistent:
|
||||
- name: Device 1
|
||||
tags:
|
||||
- device_1
|
||||
ids:
|
||||
- 2.2.2.2
|
||||
blocked_services:
|
||||
@@ -183,7 +184,7 @@ clients:
|
||||
- qq
|
||||
- vk
|
||||
- ok
|
||||
upstreams: [ ]
|
||||
upstreams: []
|
||||
use_global_settings: true
|
||||
filtering_enabled: false
|
||||
parental_enabled: false
|
||||
@@ -192,6 +193,7 @@ clients:
|
||||
ignore_querylog: false
|
||||
ignore_statistics: false
|
||||
log:
|
||||
enabled: true
|
||||
file: ""
|
||||
max_backups: 0
|
||||
max_size: 100
|
||||
@@ -203,4 +205,4 @@ os:
|
||||
group: ""
|
||||
user: ""
|
||||
rlimit_nofile: 0
|
||||
schema_version: 27
|
||||
schema_version: 28
|
||||
|
||||
93
testdata/e2e/templates/configmap-certs.yaml
vendored
Normal file
93
testdata/e2e/templates/configmap-certs.yaml
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
{{- if eq .Values.mode "env" }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: certs
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
my.crt: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE7zCCAtcCFFthw5GxzLaZC/pTTlbwju6vYr9pMA0GCSqGSIb3DQEBCwUAMDMx
|
||||
CzAJBgNVBAYTAkNIMRMwEQYDVQQIDApTb21lLVN0YXRlMQ8wDQYDVQQKDAZiYWtp
|
||||
dG8wIBcNMjQwNDA0MDUxOTMyWhgPMjEyNDAzMTEwNTE5MzJaMDMxCzAJBgNVBAYT
|
||||
AkNIMRMwEQYDVQQIDApTb21lLVN0YXRlMQ8wDQYDVQQKDAZiYWtpdG8wggIiMA0G
|
||||
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCenHJ0SunHqdtBdG8d5Uy8y5KESOPE
|
||||
GCpcjNetSR53binlV3QjVckrA+6tHQBR3yXpSgHWU424fR3eTdHsbS9/8/pAnV0V
|
||||
CAaUntbCr5CJ8ww2zRlGSz6IlxkwJkqx4crmYUcTm6XNcEm5kSoBmFR1s8TiQAQ8
|
||||
CnJyt4m3S5jSKnexAMi2l2LP4F5P5MwLHdpl2sfRYydWOoJty9nSYuRxarv7G7e3
|
||||
N102voL3W2F9Dk8QbKF1Kzxcfmh+4twNz/YfUxxB3tggrCGHfMu4EcvMrFCkKhNv
|
||||
sFcG9aZ0gNj4x54vCOMK+LuVd8JZQMcGBdcxZUq+q4nyj4dxFDKSUjZ+nVjdbuab
|
||||
dk72dVbv/r2j6aGFuZDwPeYrKOyGCUlzyPvtX3qDjrebvalkrua0ApJLTiUntQK4
|
||||
E+8P658GUzwqhKi6COEmgJgZO1+xIhO68OzPS6qj8OvgOw97VJqWsnTkDeu5Xd3E
|
||||
qW37lk62EQ75FCLaWAmyBcxTDlIWNfwvSlI57Iyw0ec9ziN4JP+6pX3QTcj69Kvc
|
||||
ERExUNGWcyQVYiCFigBqL/UtGO73PA4kb8P6Zef0oTx7siw4ysImvQ/RHvbArXPE
|
||||
iqxg/afjJz/w7fTTwk9DRW12hiWj7+ojZMcoYCj1PcOlmZvchwZRdN8Lp37wm11I
|
||||
EYxdoBQVRWk0vwIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQB6H+z2IEtiBTOIFnYJ
|
||||
++CvZWw0CLPSpuFTwpDRIVUbrfhpFoXMwb6ungaqrGNXv3H4HQXl2EwXwZomsy2K
|
||||
/pjk/0rHFdleEtJ4beSv44ToGCL+RqeU38sGuxCDQBCxIqGcbkErkqEYJqaSEV9I
|
||||
elKjBA5PEy+WO/P+J5UYU6aY+59C+ZHrZ8HzoPUGrZ6ESu9Wm/9iOpUn4DWFzOOu
|
||||
E/14QQqpHzHRvpbfOb/dsnEYv7JoiOaXhOedKmx5k+bLhNCXOJiqyqphNp0Io3Fr
|
||||
8i6IxfvOo/6nw9sQ02E6PjNyC5bRq7TUrqffTcCRq7EYhJoK43V7nzLpUBItBF7K
|
||||
FszFnvBxvgPtUdbhs6O8U8pqoPw1vCnoJLnerGfN1fEclV7Sbf3d1hCYXtHzmpZM
|
||||
qflEkJIPSgkr8dyi2JjSQR5AJUtB5/VAZwBATNKNYINfnEVzyle+64iVaKteRt9e
|
||||
yeLoy/ktTZSlQbb7/qg4NtQx9SA5PlYVMLj20S5HwStGQhLIb5lrwzkHNUZmkDWr
|
||||
LUX1ufOfhNpCH2Egn078pyEn88B5iRTga2auCk3siOs9H3wmbT2r57E7ddX3l9Sf
|
||||
MzO/8HyfNoa2RUTnJHdyduAT7VytyJSSRmfeapw9ITngZHGmpPhisKhxb6IoZm0J
|
||||
j10kyQuFfNcZKlNa3D7XFvqzWQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
my.key: |
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCenHJ0SunHqdtB
|
||||
dG8d5Uy8y5KESOPEGCpcjNetSR53binlV3QjVckrA+6tHQBR3yXpSgHWU424fR3e
|
||||
TdHsbS9/8/pAnV0VCAaUntbCr5CJ8ww2zRlGSz6IlxkwJkqx4crmYUcTm6XNcEm5
|
||||
kSoBmFR1s8TiQAQ8CnJyt4m3S5jSKnexAMi2l2LP4F5P5MwLHdpl2sfRYydWOoJt
|
||||
y9nSYuRxarv7G7e3N102voL3W2F9Dk8QbKF1Kzxcfmh+4twNz/YfUxxB3tggrCGH
|
||||
fMu4EcvMrFCkKhNvsFcG9aZ0gNj4x54vCOMK+LuVd8JZQMcGBdcxZUq+q4nyj4dx
|
||||
FDKSUjZ+nVjdbuabdk72dVbv/r2j6aGFuZDwPeYrKOyGCUlzyPvtX3qDjrebvalk
|
||||
rua0ApJLTiUntQK4E+8P658GUzwqhKi6COEmgJgZO1+xIhO68OzPS6qj8OvgOw97
|
||||
VJqWsnTkDeu5Xd3EqW37lk62EQ75FCLaWAmyBcxTDlIWNfwvSlI57Iyw0ec9ziN4
|
||||
JP+6pX3QTcj69KvcERExUNGWcyQVYiCFigBqL/UtGO73PA4kb8P6Zef0oTx7siw4
|
||||
ysImvQ/RHvbArXPEiqxg/afjJz/w7fTTwk9DRW12hiWj7+ojZMcoYCj1PcOlmZvc
|
||||
hwZRdN8Lp37wm11IEYxdoBQVRWk0vwIDAQABAoICAEx3eTh2m1F+oq1iQGXF0+Kb
|
||||
NEZhS6mQyu92mU46F52Vd05RhLS6WXNLAIjmF+7gqYrYep1FB+ifLUSs+N1GYGWM
|
||||
DqSTGTqX9XA2SaxvbrwK+GL9K0e34+x/CA4uD8nFZQf/cwBRhDRQg8KaaQl+0o0y
|
||||
P2OiYEg/8yA6OwMqx4DfJ3gmvB1HS8STU3SqBfMAD/gV60qXxnGsYJAlfJyQv7is
|
||||
L/dmTAJhByfq3gH5xLzBJr3w2UA/OWkQKjmuDk/8aBh+/XsGP+U0hy+mKyLRNZM1
|
||||
qeUTQe6RMcuxp2+4ZKI/vpPHcYorE2iCZaiY8bqGG1J9lnTpB2bw6mfKSH1BdHKB
|
||||
BCpnvMebe56hfNzQgbE5N4XZUHE4fdFRSDe0cLyagN+5BAekaA10E5cW5/7bfs4P
|
||||
VFtJgcj3wSRsnYQ4UZoA/9LE6IeQXuHauUFf9lGvy26oW8FkuTysZk6w5uxyJOew
|
||||
qM0/r7DaGZ6umAsINgLMV/2+ksVDGdOpcHmd7S6Gi26vFKIPdIXWO6BtVej6Mkb9
|
||||
vUKEEnJbx+gdiehAO63WAg9KR77bHwTXFPYGvza3mB1SXrZCGjjfBnI5yQP6VDwG
|
||||
uRa0fY/thbsS8QdHc6lmMJNKJnCyes3sG99qcpOX0HBWRpGt9YzjHMK16uQbMPn1
|
||||
0ApKa/1j/izsQtMkai81AoIBAQDLi/WbQY5jrKesRjSN7AOGND0Ge/NHF7hPIKW6
|
||||
Ql+BKiu3AiADemiXfnpeBS8OtkqxFWNFT6Ob2uv5pF2aRzP1un8mTaSipQu5iEOz
|
||||
tejyiWGMCzKaGPksB/d2spDxaDTs1KIyM+YVP2ynx8IGwet2mRcWZ4ixyQxgaj2o
|
||||
vZeCesxJZSPlt+D0NoPlthwDx4B7D49tU288WEvhPspcAjW3udE4WV4dnmFTZFtD
|
||||
ZmJ9GrW4rQsti6D2f+hnkWi17F+qGR0B7B4yyE66W8y3wzMfq0/tZzBpAH+azwjk
|
||||
qkHIbePrWTpwLwbAK4L4cKENs+yivAldfUj4kgVUhVLCscCrAoIBAQDHfBKKF/DK
|
||||
BsGvWSxo5QahJza2px1walQeE0AifkDYkLI+fyYnArOTUKKAC1/SxI21PWlPPJHz
|
||||
bt2lhF+I3+DFJTDLsUrBUN923rTpt1WpQVvBNzUFVz6JDlYr6VUOVOX0KY+LVE20
|
||||
iPmZLrutgO3JRMMFWOaT0ptV1dVx/Eyd4BiT+NfK06D4ZSHGZ6QAsuUmjc+M5VfM
|
||||
nRbuDlpMgOcBQYQSAfrA+LT6jZorv0mOO6rigNWzIP8FlsJxf1A5o5zL9CYAOOk/
|
||||
lRWI7K4LSP4TTcTW8DnXqVH7q569d2QA/RS2B1EUEdpNuT2aKIAsNNrhA3JCX6S9
|
||||
UZm9KOBwYuQ9AoIBAQCbD8ZNPkXA/RjHDryemXud01HiDK8qK5HHBfH60PF8rqma
|
||||
w02sGKZxMnL6CSzuIkUIXmi/tonHA6HdDjAYhcG5oxeWEHQpS16BOqOI1j3d9naP
|
||||
f0BPUFMSDgehLytoHKClAt+FKzBOY4Dc2Dqhdz1vnfSOptTly2lYUdcjIzu2tOHH
|
||||
z/rm14vRv23/oxn4bxUbqqDzAiqtZ/52W6VBLpXJnw8ZxEsEeVFffAZidC73a0+g
|
||||
noLzcXlwD8T2kTmZzbabGIKWok/nE92V7rUoENZze8hp7MBeXXjYcHwv5twyWjTV
|
||||
Z6YzLEASSZN+vB6VF8pftqvTwsvCQUs6Nk7z7wH9AoIBAA6EH9E+tr3syfFZmtqz
|
||||
N81ITjnyZTkF88MQgY1BBLT9qorTs9II50pkBr8slLeAqBM1OdGTRceiHKzrugv6
|
||||
xp9x+mAIMblpiilbQWz0c15SrDueKdSOqbVNfsXJP/BAC0++KnzoEJN/mDImbW/N
|
||||
vv/zagGcm4LMQ5N2cQbPZj/iy8cQx5sx1TfeHBwU9KE8Y2Jv1VeaZM417DI8hyOk
|
||||
CatUuiiZTkb2kizdWwet7stT2jaLS4Gyd/xPIS0jJ5JaLpHE3XMMsSR4U83X8z5M
|
||||
/HgpI5bEemEQKDAZJ/7/jh5oTDaGx8afGfSn8yyhn9oXqonPN2RPE2zXYEmcjOCA
|
||||
wb0CggEBAI9ztuEtsucOVKa52Itzvk8sZn2MF1kpUPHGF6/VW6a/s45wvvFFW3PE
|
||||
BjqmgJX8s2ZLgOgWE/wEmEpHX+lxmebYJ3bmOsx5eGfA1eZBypWCBYBSfezf/1zD
|
||||
3AD65+gwO4GhnSN4zsrlyVC6SsWEaeu+sxexHt15YJI9SkwCkvZTYoLqe9nMcAjY
|
||||
h41p/i6bhl9ODZPd1+hL631EFWguyMVSVTDgIG141OIYKz5hVQZ08ddrwHB2gPf6
|
||||
JSdU2DFOD6tNCfvTYAMvUfkoIt3g9RDJ5dTdR9J/3efm0ZIutcYcHiASIcubPU9x
|
||||
r8Ww1tZIoBx/s50i7X1JFyuXp0TWQsI=
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
{{- end }}
|
||||
@@ -5,7 +5,13 @@ metadata:
|
||||
name: sync-conf
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
API_PORT: '0'
|
||||
API_PORT: '9090'
|
||||
API_METRICS_ENABLED: 'true'
|
||||
API_METRICS_SCRAPE_INTERVAL: '30s'
|
||||
API_TLS_CERT_DIR: '/certs'
|
||||
API_TLS_CERT_NAME: 'my.crt'
|
||||
API_TLS_KEY_NAME: 'my.key'
|
||||
LOG_FORMAT: 'json'
|
||||
ORIGIN_URL: 'http://service-origin.{{ $.Release.Namespace }}.svc.cluster.local:3000'
|
||||
ORIGIN_PASSWORD: 'password'
|
||||
ORIGIN_USERNAME: 'username'
|
||||
|
||||
@@ -11,7 +11,10 @@ data:
|
||||
username: username
|
||||
password: password
|
||||
api:
|
||||
port: 0
|
||||
port: 9090
|
||||
metrics:
|
||||
enabled: true
|
||||
scrapeInterval: 30s
|
||||
replicas:
|
||||
{{- range $i,$version := .Values.replica.versions }}
|
||||
- url: 'http://service-replica-{{ $version | toString | replace "." "-" }}.{{ $.Release.Namespace }}.svc.cluster.local:3000'
|
||||
|
||||
@@ -26,5 +26,12 @@ spec:
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: sync-conf
|
||||
volumeMounts:
|
||||
- name: certs
|
||||
mountPath: /certs
|
||||
restartPolicy: Never
|
||||
volumes:
|
||||
- name: certs
|
||||
configMap:
|
||||
name: certs
|
||||
{{- end }}
|
||||
|
||||
2
testdata/e2e/values.yaml
vendored
2
testdata/e2e/values.yaml
vendored
@@ -8,4 +8,4 @@ mode: env
|
||||
|
||||
kubectl:
|
||||
repository: bitnami/kubectl
|
||||
tag: 1.27
|
||||
tag: "1.30"
|
||||
|
||||
8
testdata/querylog_config.json
vendored
Normal file
8
testdata/querylog_config.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"interval": 90,
|
||||
"anonymize_client_ip": false,
|
||||
"ignored": [
|
||||
"foo.bar"
|
||||
]
|
||||
}
|
||||
5
testdata/querylog_info.json
vendored
5
testdata/querylog_info.json
vendored
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"interval": 90,
|
||||
"anonymize_client_ip": false
|
||||
}
|
||||
Reference in New Issue
Block a user