Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32c728ccf8 | ||
|
|
327d67c9ef | ||
|
|
edd7f82351 | ||
|
|
012d43b950 | ||
|
|
e2298f6f1e | ||
|
|
18350f94a6 | ||
|
|
1a9d5df5cb | ||
|
|
8ef1eb8c2a | ||
|
|
7c2018acbc | ||
|
|
2079f6a3eb | ||
|
|
d9419446bf | ||
|
|
c0cbccb63c | ||
|
|
5444dda08a | ||
|
|
224617aae6 | ||
|
|
c941c8a100 | ||
|
|
07a25cd094 | ||
|
|
54d98c10fe | ||
|
|
4a897c40b5 | ||
|
|
9af45d3cab | ||
|
|
9c60b399a8 | ||
|
|
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 |
4
.github/workflows/codeql-analysis.yml
vendored
4
.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
|
||||
|
||||
@@ -4,10 +4,17 @@ 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,7 +51,7 @@ 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@v6
|
||||
if: ${{ github.event.release.tag_name != '' }}
|
||||
@@ -63,7 +70,7 @@ 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@v6
|
||||
if: ${{ github.event.release.tag_name == '' && env.NEW_COMMIT_COUNT > 0 }}
|
||||
4
.github/workflows/e2e.yaml
vendored
4
.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
|
||||
|
||||
4
.github/workflows/go.yml
vendored
4
.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
|
||||
|
||||
16
.github/workflows/virustotal.yaml
vendored
Normal file
16
.github/workflows/virustotal.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: Scan GitHub Release with VirusTotal
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
scan_release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Analyze Build Assets
|
||||
uses: bakito/virustotal-action@main
|
||||
with:
|
||||
release_name: ${{github.event.release.tag_name}}
|
||||
vt_api_key: ${{secrets.VT_API_KEY}}
|
||||
@@ -42,5 +42,6 @@ changelog:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^chore'
|
||||
release:
|
||||
prerelease: auto
|
||||
|
||||
24
.toolbox.mk
24
.toolbox.mk
@@ -11,7 +11,9 @@ $(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
|
||||
@@ -19,11 +21,15 @@ TB_SEMVER ?= $(TB_LOCALBIN)/semver
|
||||
|
||||
## Tool Versions
|
||||
# renovate: packageName=k8s.io/code-generator/cmd/deepcopy-gen
|
||||
TB_DEEPCOPY_GEN_VERSION ?= v0.32.0
|
||||
TB_DEEPCOPY_GEN_VERSION ?= v0.32.3
|
||||
# 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.62.2
|
||||
TB_GOLANGCI_LINT_VERSION ?= v1.64.7
|
||||
# renovate: packageName=github.com/segmentio/golines
|
||||
TB_GOLINES_VERSION ?= v0.12.2
|
||||
# renovate: packageName=github.com/goreleaser/goreleaser/v2
|
||||
TB_GORELEASER_VERSION ?= v2.5.0
|
||||
TB_GORELEASER_VERSION ?= v2.8.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
|
||||
@@ -40,10 +46,18 @@ $(TB_DEEPCOPY_GEN): $(TB_LOCALBIN)
|
||||
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)
|
||||
@@ -67,7 +81,9 @@ 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 \
|
||||
@@ -78,7 +94,9 @@ tb.reset:
|
||||
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 \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.23-alpine AS builder
|
||||
FROM golang:1.24-alpine AS builder
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
|
||||
10
Makefile
10
Makefile
@@ -14,8 +14,14 @@ generate: tb.deepcopy-gen
|
||||
@touch ./tmp/deepcopy-gen-boilerplate.go.txt
|
||||
$(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
|
||||
|
||||
fuzz:
|
||||
go test -fuzz=FuzzMask -v ./pkg/types/ -fuzztime=60s
|
||||
|
||||
# Run ci tests
|
||||
test-ci: mocks tidy tb.ginkgo
|
||||
@@ -65,7 +71,7 @@ kind-test:
|
||||
@./testdata/e2e/bin/install-chart.sh
|
||||
|
||||
# renovate: packageName=AdguardTeam/AdGuardHome
|
||||
ADGUARD_HOME_VERSION ?= v0.107.55
|
||||
ADGUARD_HOME_VERSION ?= v0.107.57
|
||||
|
||||
model: tb.oapi-codegen
|
||||
@mkdir -p tmp
|
||||
|
||||
@@ -245,6 +245,11 @@ services:
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Unraid
|
||||
|
||||
Note when running the Docker container in Unraid please remove unneeded env variables if don't needed.
|
||||
If replica2 isn't used this can cause sync errors.
|
||||
|
||||
### Config file
|
||||
|
||||
location: $HOME/.adguardhome-sync.yaml
|
||||
|
||||
26
cmd/run.go
26
cmd/run.go
@@ -5,7 +5,6 @@ import (
|
||||
"github.com/bakito/adguardhome-sync/pkg/log"
|
||||
"github.com/bakito/adguardhome-sync/pkg/sync"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// runCmd represents the run command
|
||||
@@ -26,18 +25,16 @@ var doCmd = &cobra.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
if cfg.PrintConfigOnly {
|
||||
config, err := yaml.Marshal(cfg)
|
||||
if err != nil {
|
||||
if cfg.PrintConfigOnly() {
|
||||
if err := cfg.Print(); err != nil {
|
||||
logger.Error(err)
|
||||
return err
|
||||
}
|
||||
logger.Infof("Printing adguardhome-sync config (THE APPLICATION WILL NOT START IN THIS MODE): \n%s",
|
||||
string(config))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return sync.Sync(cfg)
|
||||
return sync.Sync(cfg.Get())
|
||||
},
|
||||
}
|
||||
|
||||
@@ -50,7 +47,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 +68,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 +77,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")
|
||||
}
|
||||
|
||||
41
go.mod
41
go.mod
@@ -1,25 +1,26 @@
|
||||
module github.com/bakito/adguardhome-sync
|
||||
|
||||
go 1.23.1
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/caarlos0/env/v11 v11.2.2
|
||||
github.com/caarlos0/env/v11 v11.3.1
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/go-resty/resty/v2 v2.16.2
|
||||
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.22.0
|
||||
github.com/onsi/gomega v1.36.1
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/onsi/ginkgo/v2 v2.23.0
|
||||
github.com/onsi/gomega v1.36.2
|
||||
github.com/prometheus/client_golang v1.21.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/spf13/cobra v1.8.1
|
||||
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/exp v0.0.0-20241215155358-4a5509556b9e
|
||||
golang.org/x/mod v0.22.0
|
||||
golang.org/x/mod v0.24.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/apimachinery v0.32.0
|
||||
k8s.io/apimachinery v0.32.3
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758
|
||||
)
|
||||
|
||||
@@ -43,10 +44,10 @@ require (
|
||||
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-20241029153458-d1b30febd7db // 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/compress v1.17.9 // 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
|
||||
@@ -55,20 +56,20 @@ require (
|
||||
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.55.0 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // 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.8.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/net v0.32.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/tools v0.28.0 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
golang.org/x/crypto v0.35.0 // indirect
|
||||
golang.org/x/net v0.36.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/tools v0.30.0 // indirect
|
||||
google.golang.org/protobuf v1.36.1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
|
||||
89
go.sum
89
go.sum
@@ -8,19 +8,21 @@ github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc
|
||||
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.2.2 h1:95fApNrUyueipoZN/EhA8mMxiNxrBwDa+oAZrMWl3Kg=
|
||||
github.com/caarlos0/env/v11 v11.2.2/go.mod h1:JBfcdeQiBoI3Zh1QRAWfe+tpiNTmDtcCj/hHHHMx0vc=
|
||||
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.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
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=
|
||||
@@ -29,6 +31,8 @@ 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.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=
|
||||
@@ -39,8 +43,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
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.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg=
|
||||
github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
|
||||
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=
|
||||
@@ -53,8 +57,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||
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-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
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=
|
||||
@@ -66,8 +70,8 @@ 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.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
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.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
@@ -91,21 +95,21 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
||||
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.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
|
||||
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
|
||||
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/onsi/ginkgo/v2 v2.23.0 h1:FA1xjp8ieYDzlgS5ABTpdUDB7wtngggONc8a7ku2NqQ=
|
||||
github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||
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.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
|
||||
github.com/prometheus/client_golang v1.21.1/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.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
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=
|
||||
@@ -113,10 +117,12 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
|
||||
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.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
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=
|
||||
@@ -128,8 +134,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
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=
|
||||
@@ -152,22 +159,18 @@ 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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
|
||||
golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4=
|
||||
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
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.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
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.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
||||
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||
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=
|
||||
@@ -176,26 +179,26 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
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.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
|
||||
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
|
||||
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||
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.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -204,12 +207,10 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
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.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg=
|
||||
k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
|
||||
k8s.io/apimachinery v0.32.3/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-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
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=
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -51,6 +53,11 @@ func main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
correctEntries(schema)
|
||||
|
||||
addFakeTags(schema)
|
||||
|
||||
b, err := yaml.Marshal(&schema)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
@@ -61,3 +68,26 @@ func main() {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
func correctEntries(schema map[string]interface{}) {
|
||||
// https://github.com/AdguardTeam/AdGuardHome/pull/7678
|
||||
if err := unstructured.SetNestedField(schema, "string", "components", "schemas", "QueryLogItem", "properties", "client_proto", "type"); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
func addFakeTags(schema map[string]interface{}) {
|
||||
fake := map[string]interface{}{"faker": `slice_len=24`}
|
||||
if err := unstructured.SetNestedMap(schema, fake, "components", "schemas", "Stats", "properties", "blocked_filtering", "x-oapi-codegen-extra-tags"); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if err := unstructured.SetNestedMap(schema, fake, "components", "schemas", "Stats", "properties", "dns_queries", "x-oapi-codegen-extra-tags"); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if err := unstructured.SetNestedMap(schema, fake, "components", "schemas", "Stats", "properties", "replaced_parental", "x-oapi-codegen-extra-tags"); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if err := unstructured.SetNestedMap(schema, fake, "components", "schemas", "Stats", "properties", "replaced_safebrowsing", "x-oapi-codegen-extra-tags"); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +166,10 @@ func (cl *client) Stats() (*model.Stats, error) {
|
||||
|
||||
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))
|
||||
err := cl.doGet(
|
||||
cl.client.R().EnableTrace().SetResult(ql),
|
||||
fmt.Sprintf(`querylog?limit=%d&response_status="all"`, limit),
|
||||
)
|
||||
return ql, err
|
||||
}
|
||||
|
||||
@@ -260,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 {
|
||||
@@ -309,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 {
|
||||
@@ -324,7 +333,8 @@ func (cl *client) QueryLogConfig() (*model.QueryLogConfigWithIgnored, error) {
|
||||
}
|
||||
|
||||
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")
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
@@ -137,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())
|
||||
})
|
||||
@@ -303,7 +310,10 @@ var _ = Describe("Client", func() {
|
||||
Ω(*qlc.Interval).Should(Equal(model.QueryLogConfigInterval(90)))
|
||||
})
|
||||
It("should set QueryLogConfig", func() {
|
||||
ts, cl = ClientPut("/querylog/config/update", `{"anonymize_client_ip":true,"enabled":true,"interval":123,"ignored":["foo.bar"]}`)
|
||||
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.QueryLogConfigWithIgnored{
|
||||
|
||||
@@ -442,7 +442,9 @@ func (c *DNSConfig) Sanitize(l *zap.SugaredLogger) {
|
||||
// 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")
|
||||
l.Warn(
|
||||
"disabling replica 'Use private reverse DNS resolvers' as no 'Private reverse DNS servers' are configured on origin",
|
||||
)
|
||||
c.UsePrivatePtrResolvers = utils.Ptr(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,15 @@ const (
|
||||
QueryLogConfigIntervalN90 QueryLogConfigInterval = 90
|
||||
)
|
||||
|
||||
// Defines values for QueryLogItemClientProto.
|
||||
const (
|
||||
Dnscrypt QueryLogItemClientProto = "dnscrypt"
|
||||
Doh QueryLogItemClientProto = "doh"
|
||||
Doq QueryLogItemClientProto = "doq"
|
||||
Dot QueryLogItemClientProto = "dot"
|
||||
Empty QueryLogItemClientProto = ""
|
||||
)
|
||||
|
||||
// Defines values for QueryLogItemReason.
|
||||
const (
|
||||
QueryLogItemReasonFilteredBlackList QueryLogItemReason = "FilteredBlackList"
|
||||
@@ -397,6 +406,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.
|
||||
@@ -443,8 +463,11 @@ type DNSConfig struct {
|
||||
UpstreamDnsFile *string `json:"upstream_dns_file,omitempty"`
|
||||
|
||||
// UpstreamMode Upstream modes enumeration.
|
||||
UpstreamMode *DNSConfigUpstreamMode `json:"upstream_mode,omitempty"`
|
||||
UsePrivatePtrResolvers *bool `json:"use_private_ptr_resolvers,omitempty"`
|
||||
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.
|
||||
@@ -725,11 +748,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.
|
||||
@@ -791,8 +821,8 @@ type QueryLogItem struct {
|
||||
ClientId *string `json:"client_id,omitempty"`
|
||||
|
||||
// ClientInfo Client information for a query log item.
|
||||
ClientInfo *QueryLogItemClient `json:"client_info,omitempty"`
|
||||
ClientProto *interface{} `json:"client_proto,omitempty"`
|
||||
ClientInfo *QueryLogItemClient `json:"client_info,omitempty"`
|
||||
ClientProto *QueryLogItemClientProto `json:"client_proto,omitempty"`
|
||||
|
||||
// Ecs The IP network defined by an EDNS Client-Subnet option in the request message if any.
|
||||
Ecs *string `json:"ecs,omitempty"`
|
||||
@@ -833,6 +863,9 @@ type QueryLogItem struct {
|
||||
Upstream *string `json:"upstream,omitempty"`
|
||||
}
|
||||
|
||||
// QueryLogItemClientProto defines model for QueryLogItem.ClientProto.
|
||||
type QueryLogItemClientProto string
|
||||
|
||||
// QueryLogItemReason Request filtering status.
|
||||
type QueryLogItemReason string
|
||||
|
||||
@@ -968,8 +1001,8 @@ type SetRulesRequest struct {
|
||||
type Stats struct {
|
||||
// AvgProcessingTime Average time in seconds on processing a DNS request
|
||||
AvgProcessingTime *float32 `json:"avg_processing_time,omitempty"`
|
||||
BlockedFiltering *[]int `json:"blocked_filtering,omitempty"`
|
||||
DnsQueries *[]int `json:"dns_queries,omitempty"`
|
||||
BlockedFiltering *[]int `faker:"slice_len=24" json:"blocked_filtering,omitempty"`
|
||||
DnsQueries *[]int `faker:"slice_len=24" json:"dns_queries,omitempty"`
|
||||
|
||||
// NumBlockedFiltering Number of requests blocked by filtering rules
|
||||
NumBlockedFiltering *int `json:"num_blocked_filtering,omitempty"`
|
||||
@@ -985,8 +1018,8 @@ type Stats struct {
|
||||
|
||||
// NumReplacedSafesearch Number of requests blocked by safesearch module
|
||||
NumReplacedSafesearch *int `json:"num_replaced_safesearch,omitempty"`
|
||||
ReplacedParental *[]int `json:"replaced_parental,omitempty"`
|
||||
ReplacedSafebrowsing *[]int `json:"replaced_safebrowsing,omitempty"`
|
||||
ReplacedParental *[]int `faker:"slice_len=24" json:"replaced_parental,omitempty"`
|
||||
ReplacedSafebrowsing *[]int `faker:"slice_len=24" json:"replaced_safebrowsing,omitempty"`
|
||||
|
||||
// TimeUnits Time units
|
||||
TimeUnits *StatsTimeUnits `json:"time_units,omitempty"`
|
||||
@@ -1205,6 +1238,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
|
||||
|
||||
@@ -1494,6 +1530,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)
|
||||
|
||||
@@ -1986,6 +2027,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 {
|
||||
@@ -3674,6 +3739,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
|
||||
@@ -5984,6 +6089,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)
|
||||
|
||||
@@ -6560,6 +6670,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
|
||||
@@ -8099,6 +8231,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...)
|
||||
@@ -9244,6 +9393,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)
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
@@ -311,8 +314,14 @@ var _ = Describe("Types", func() {
|
||||
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")}}
|
||||
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() {
|
||||
|
||||
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"
|
||||
}
|
||||
@@ -14,16 +14,39 @@ var (
|
||||
logger = log.GetLogger("config")
|
||||
)
|
||||
|
||||
func Get(configFile string, flags Flags) (*types.Config, error) {
|
||||
type AppConfig struct {
|
||||
cfg *types.Config
|
||||
filePath string
|
||||
content string
|
||||
}
|
||||
|
||||
func (ac *AppConfig) PrintConfigOnly() bool {
|
||||
return ac.cfg.PrintConfigOnly
|
||||
}
|
||||
|
||||
func (ac *AppConfig) Get() *types.Config {
|
||||
return ac.cfg
|
||||
}
|
||||
|
||||
func (ac *AppConfig) Init() error {
|
||||
return ac.cfg.Init()
|
||||
}
|
||||
|
||||
func Get(configFile string, flags Flags) (*AppConfig, error) {
|
||||
path, err := configFilePath(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = validateSchema(path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := initialConfig()
|
||||
|
||||
// read yaml config
|
||||
if err := readFile(cfg, path); err != nil {
|
||||
var content string
|
||||
if content, err = readFile(cfg, path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -82,7 +105,7 @@ func Get(configFile string, flags Flags) (*types.Config, error) {
|
||||
|
||||
cfg.Replicas, err = enrichReplicasFromEnv(cfg.Replicas)
|
||||
|
||||
return cfg, err
|
||||
return &AppConfig{cfg: cfg, filePath: path, content: content}, err
|
||||
}
|
||||
|
||||
func initialConfig() *types.Config {
|
||||
|
||||
@@ -49,7 +49,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Origin.URL).Should(Equal("https://origin-file:443"))
|
||||
Ω(cfg.Get().Origin.URL).Should(Equal("https://origin-file:443"))
|
||||
})
|
||||
It("should have the origin URL from the config flags", func() {
|
||||
flags.EXPECT().Changed(config.FlagOriginURL).Return(true).AnyTimes()
|
||||
@@ -58,7 +58,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Origin.URL).Should(Equal("https://origin-flag:443"))
|
||||
Ω(cfg.Get().Origin.URL).Should(Equal("https://origin-flag:443"))
|
||||
})
|
||||
It("should have the origin URL from the config env var", func() {
|
||||
setEnv("ORIGIN_URL", "https://origin-env:443")
|
||||
@@ -68,7 +68,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Origin.URL).Should(Equal("https://origin-env:443"))
|
||||
Ω(cfg.Get().Origin.URL).Should(Equal("https://origin-env:443"))
|
||||
})
|
||||
})
|
||||
Context("Replica insecure skip verify", func() {
|
||||
@@ -77,7 +77,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replica.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Replicas[0].InsecureSkipVerify).Should(BeFalse())
|
||||
Ω(cfg.Get().Replicas[0].InsecureSkipVerify).Should(BeFalse())
|
||||
})
|
||||
It("should have the insecure skip verify from the config flags", func() {
|
||||
flags.EXPECT().Changed(config.FlagReplicaISV).Return(true).AnyTimes()
|
||||
@@ -86,7 +86,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replica.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Replicas[0].InsecureSkipVerify).Should(BeTrue())
|
||||
Ω(cfg.Get().Replicas[0].InsecureSkipVerify).Should(BeTrue())
|
||||
})
|
||||
It("should have the insecure skip verify from the config env var", func() {
|
||||
setEnv("REPLICA_INSECURE_SKIP_VERIFY", "false")
|
||||
@@ -96,7 +96,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replica.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Replicas[0].InsecureSkipVerify).Should(BeFalse())
|
||||
Ω(cfg.Get().Replicas[0].InsecureSkipVerify).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -106,7 +106,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Replicas[0].InsecureSkipVerify).Should(BeFalse())
|
||||
Ω(cfg.Get().Replicas[0].InsecureSkipVerify).Should(BeFalse())
|
||||
})
|
||||
It("should have the insecure skip verify from the config env var", func() {
|
||||
setEnv("REPLICA1_INSECURE_SKIP_VERIFY", "true")
|
||||
@@ -114,7 +114,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Replicas[0].InsecureSkipVerify).Should(BeTrue())
|
||||
Ω(cfg.Get().Replicas[0].InsecureSkipVerify).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
Context("API Port", func() {
|
||||
@@ -122,7 +122,7 @@ var _ = Describe("Config", 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))
|
||||
Ω(cfg.Get().API.Port).Should(Equal(9090))
|
||||
})
|
||||
It("should have the api port from the config flags", func() {
|
||||
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes()
|
||||
@@ -131,7 +131,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.API.Port).Should(Equal(9990))
|
||||
Ω(cfg.Get().API.Port).Should(Equal(9990))
|
||||
})
|
||||
It("should have the api port from the config env var", func() {
|
||||
setEnv("API_PORT", "9999")
|
||||
@@ -141,7 +141,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.API.Port).Should(Equal(9999))
|
||||
Ω(cfg.Get().API.Port).Should(Equal(9999))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -151,8 +151,8 @@ var _ = Describe("Config", func() {
|
||||
|
||||
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())
|
||||
Ω(cfg.Get().Replicas[0].DHCPServerEnabled).ShouldNot(BeNil())
|
||||
Ω(*cfg.Get().Replicas[0].DHCPServerEnabled).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -162,8 +162,8 @@ var _ = Describe("Config", func() {
|
||||
|
||||
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())
|
||||
Ω(cfg.Get().Replicas[0].DHCPServerEnabled).ShouldNot(BeNil())
|
||||
Ω(*cfg.Get().Replicas[0].DHCPServerEnabled).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
Context("API Port", func() {
|
||||
@@ -171,7 +171,7 @@ var _ = Describe("Config", 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))
|
||||
Ω(cfg.Get().API.Port).Should(Equal(9090))
|
||||
})
|
||||
It("should have the api port from the config flags", func() {
|
||||
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes()
|
||||
@@ -180,7 +180,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.API.Port).Should(Equal(9990))
|
||||
Ω(cfg.Get().API.Port).Should(Equal(9990))
|
||||
})
|
||||
It("should have the api port from the config env var", func() {
|
||||
setEnv("API_PORT", "9999")
|
||||
@@ -190,18 +190,16 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.API.Port).Should(Equal(9999))
|
||||
Ω(cfg.Get().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()
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Features.DNS.ServerConfig).Should(BeFalse())
|
||||
Ω(cfg.Get().Features.DNS.ServerConfig).Should(BeFalse())
|
||||
})
|
||||
It("should have the feature dns server config from the config flags", func() {
|
||||
flags.EXPECT().Changed(config.FlagFeatureDnsServerConfig).Return(true).AnyTimes()
|
||||
@@ -210,7 +208,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Features.DNS.ServerConfig).Should(BeTrue())
|
||||
Ω(cfg.Get().Features.DNS.ServerConfig).Should(BeTrue())
|
||||
})
|
||||
It("should have the feature dns server config from the config env var", func() {
|
||||
setEnv("FEATURES_DNS_SERVER_CONFIG", "false")
|
||||
@@ -220,7 +218,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Features.DNS.ServerConfig).Should(BeFalse())
|
||||
Ω(cfg.Get().Features.DNS.ServerConfig).Should(BeFalse())
|
||||
})
|
||||
It("should have the feature dns server config from the config DEPRECATED env var", func() {
|
||||
setEnv("FEATURES_DNS_SERVERCONFIG", "false")
|
||||
@@ -230,7 +228,7 @@ var _ = Describe("Config", func() {
|
||||
|
||||
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Features.DNS.ServerConfig).Should(BeFalse())
|
||||
Ω(cfg.Get().Features.DNS.ServerConfig).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/config"
|
||||
"github.com/bakito/adguardhome-sync/pkg/types"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
@@ -99,7 +98,7 @@ var _ = Describe("Config", func() {
|
||||
Ω(os.Setenv(fmt.Sprintf("REPLICA%s_INTERFACE_NAME", "1"), "eth0")).ShouldNot(HaveOccurred())
|
||||
cfg, err := config.Get("", nil)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Replicas[0].InterfaceName).Should(Equal("eth0"))
|
||||
Ω(cfg.Get().Replicas[0].InterfaceName).Should(Equal("eth0"))
|
||||
})
|
||||
})
|
||||
Context("dhcp server", func() {
|
||||
@@ -108,32 +107,32 @@ var _ = Describe("Config", func() {
|
||||
Ω(os.Setenv(fmt.Sprintf("REPLICA%s_DHCPSERVERENABLED", "1"), "true")).ShouldNot(HaveOccurred())
|
||||
cfg, err := config.Get("", nil)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Replicas[0].DHCPServerEnabled).ShouldNot(BeNil())
|
||||
Ω(*cfg.Replicas[0].DHCPServerEnabled).Should(BeTrue())
|
||||
Ω(cfg.Get().Replicas[0].DHCPServerEnabled).ShouldNot(BeNil())
|
||||
Ω(*cfg.Get().Replicas[0].DHCPServerEnabled).Should(BeTrue())
|
||||
})
|
||||
It("should disable the dhcp server of replica 1", func() {
|
||||
Ω(os.Setenv("REPLICA1_URL", "https://foo.bar")).ShouldNot(HaveOccurred())
|
||||
Ω(os.Setenv(fmt.Sprintf("REPLICA%s_DHCPSERVERENABLED", "1"), "false")).ShouldNot(HaveOccurred())
|
||||
cfg, err := config.Get("", nil)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(cfg.Replicas[0].DHCPServerEnabled).ShouldNot(BeNil())
|
||||
Ω(*cfg.Replicas[0].DHCPServerEnabled).Should(BeFalse())
|
||||
Ω(cfg.Get().Replicas[0].DHCPServerEnabled).ShouldNot(BeNil())
|
||||
Ω(*cfg.Get().Replicas[0].DHCPServerEnabled).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func verifyFeatures(cfg *types.Config, value bool) {
|
||||
Ω(cfg.Features.GeneralSettings).Should(Equal(value))
|
||||
Ω(cfg.Features.QueryLogConfig).Should(Equal(value))
|
||||
Ω(cfg.Features.StatsConfig).Should(Equal(value))
|
||||
Ω(cfg.Features.ClientSettings).Should(Equal(value))
|
||||
Ω(cfg.Features.Services).Should(Equal(value))
|
||||
Ω(cfg.Features.Filters).Should(Equal(value))
|
||||
Ω(cfg.Features.DHCP.ServerConfig).Should(Equal(value))
|
||||
Ω(cfg.Features.DHCP.StaticLeases).Should(Equal(value))
|
||||
Ω(cfg.Features.DNS.ServerConfig).Should(Equal(value))
|
||||
Ω(cfg.Features.DNS.AccessLists).Should(Equal(value))
|
||||
Ω(cfg.Features.DNS.Rewrites).Should(Equal(value))
|
||||
func verifyFeatures(cfg *config.AppConfig, value bool) {
|
||||
Ω(cfg.Get().Features.GeneralSettings).Should(Equal(value))
|
||||
Ω(cfg.Get().Features.QueryLogConfig).Should(Equal(value))
|
||||
Ω(cfg.Get().Features.StatsConfig).Should(Equal(value))
|
||||
Ω(cfg.Get().Features.ClientSettings).Should(Equal(value))
|
||||
Ω(cfg.Get().Features.Services).Should(Equal(value))
|
||||
Ω(cfg.Get().Features.Filters).Should(Equal(value))
|
||||
Ω(cfg.Get().Features.DHCP.ServerConfig).Should(Equal(value))
|
||||
Ω(cfg.Get().Features.DHCP.StaticLeases).Should(Equal(value))
|
||||
Ω(cfg.Get().Features.DNS.ServerConfig).Should(Equal(value))
|
||||
Ω(cfg.Get().Features.DNS.AccessLists).Should(Equal(value))
|
||||
Ω(cfg.Get().Features.DNS.Rewrites).Should(Equal(value))
|
||||
}
|
||||
|
||||
@@ -8,17 +8,19 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func readFile(cfg *types.Config, path string) error {
|
||||
func readFile(cfg *types.Config, path string) (string, error) {
|
||||
var content string
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
content = string(b)
|
||||
if err := yaml.Unmarshal(b, cfg); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func configFilePath(configFile string) (string, error) {
|
||||
|
||||
90
pkg/config/print-config.go
Normal file
90
pkg/config/print-config.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/client"
|
||||
"github.com/bakito/adguardhome-sync/pkg/types"
|
||||
"github.com/bakito/adguardhome-sync/version"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
//go:embed print-config.md
|
||||
var printConfigTemplate string
|
||||
|
||||
func (ac *AppConfig) Print() error {
|
||||
originVersion := aghVersion(ac.cfg.Origin)
|
||||
var replicaVersions []string
|
||||
for _, replica := range ac.cfg.Replicas {
|
||||
replicaVersions = append(replicaVersions, aghVersion(replica))
|
||||
}
|
||||
|
||||
out, err := ac.print(os.Environ(), originVersion, replicaVersions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Infof(
|
||||
"Printing adguardhome-sync aggregated config (THE APPLICATION WILL NOT START IN THIS MODE):\n%s",
|
||||
out,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func aghVersion(i types.AdGuardInstance) string {
|
||||
cl, err := client.New(i)
|
||||
if err != nil {
|
||||
return "N/A"
|
||||
}
|
||||
stats, err := cl.Status()
|
||||
if err != nil {
|
||||
return "N/A"
|
||||
}
|
||||
return stats.Version
|
||||
}
|
||||
|
||||
func (ac *AppConfig) print(env []string, originVersion string, replicaVersions []string) (string, error) {
|
||||
config, err := yaml.Marshal(ac.Get())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
funcMap := template.FuncMap{
|
||||
// The name "inc" is what the function will be called in the template text.
|
||||
"inc": func(i int) int {
|
||||
return i + 1
|
||||
},
|
||||
}
|
||||
|
||||
t, err := template.New("printConfigTemplate").Funcs(funcMap).Parse(printConfigTemplate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sort.Strings(env)
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
if err = t.Execute(&buf, map[string]interface{}{
|
||||
"Version": version.Version,
|
||||
"Build": version.Build,
|
||||
"OperatingSystem": runtime.GOOS,
|
||||
"Architecture": runtime.GOARCH,
|
||||
"AggregatedConfig": string(config),
|
||||
"ConfigFilePath": ac.filePath,
|
||||
"ConfigFileContent": ac.content,
|
||||
"EnvironmentVariables": strings.Join(env, "\n"),
|
||||
"OriginVersion": originVersion,
|
||||
"ReplicaVersions": replicaVersions,
|
||||
}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
36
pkg/config/print-config.md
Normal file
36
pkg/config/print-config.md
Normal file
@@ -0,0 +1,36 @@
|
||||
<!-- PLEASE COPY THE FOLLOWING OUTPUT AS IS INTO THE GITHUB ISSUE (Don't forget to mask your usernames, passwords, IPs and other sensitive information when using this in an issue ) -->
|
||||
|
||||
### Runtime
|
||||
|
||||
AdguardHome-Sync Version: {{ .Version }}
|
||||
Build: {{ .Build }}
|
||||
OperatingSystem: {{ .OperatingSystem }}
|
||||
Architecture: {{ .Architecture }}
|
||||
OriginVersion: {{ .OriginVersion }}
|
||||
ReplicaVersions:
|
||||
{{- range $i,$rep := .ReplicaVersions }}
|
||||
- Replica {{ inc $i }}: {{ $rep }}
|
||||
{{- end }}
|
||||
|
||||
### AdGuardHome sync aggregated config
|
||||
|
||||
```yaml
|
||||
{{ .AggregatedConfig }}
|
||||
```
|
||||
{{- if .ConfigFilePath }}
|
||||
### AdGuardHome sync unmodified config file
|
||||
|
||||
Config file path: {{ .ConfigFilePath }}
|
||||
|
||||
```yaml
|
||||
{{ .ConfigFileContent }}
|
||||
```
|
||||
{{- end }}
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```ini
|
||||
{{ .EnvironmentVariables }}
|
||||
```
|
||||
|
||||
<!-- END OF GITHUB ISSUE CONTENT -->
|
||||
58
pkg/config/print-config_test.go
Normal file
58
pkg/config/print-config_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/types"
|
||||
"github.com/bakito/adguardhome-sync/version"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("AppConfig", func() {
|
||||
var (
|
||||
ac *AppConfig
|
||||
env []string
|
||||
)
|
||||
BeforeEach(func() {
|
||||
ac = &AppConfig{
|
||||
cfg: &types.Config{
|
||||
Origin: types.AdGuardInstance{
|
||||
URL: "https://ha.xxxx.net:3000",
|
||||
},
|
||||
},
|
||||
content: `
|
||||
origin:
|
||||
url: https://ha.xxxx.net:3000
|
||||
`,
|
||||
}
|
||||
env = []string{"FOO=foo", "BAR=bar"}
|
||||
})
|
||||
Context("print", func() {
|
||||
It("should print config without file", func() {
|
||||
out, err := ac.print(env, "v0.0.1", []string{"v0.0.2"})
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(
|
||||
out,
|
||||
).Should(Equal(fmt.Sprintf(expected(1), version.Version, version.Build, runtime.GOOS, runtime.GOARCH)))
|
||||
})
|
||||
It("should print config with file", func() {
|
||||
ac.filePath = "config.yaml"
|
||||
out, err := ac.print(env, "v0.0.1", []string{"v0.0.2"})
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(
|
||||
out,
|
||||
).Should(Equal(fmt.Sprintf(expected(2), version.Version, version.Build, runtime.GOOS, runtime.GOARCH)))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func expected(id int) string {
|
||||
b, err := os.ReadFile(filepath.Join("../../testdata/config", fmt.Sprintf("print-config_test_expected%d.md", id)))
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
return string(b)
|
||||
}
|
||||
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())
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/bakito/adguardhome-sync/pkg/client/model"
|
||||
"github.com/bakito/adguardhome-sync/pkg/log"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
const StatsTotal = "total"
|
||||
@@ -156,8 +155,8 @@ func initMetric(name string, metric *prometheus.GaugeVec) {
|
||||
l.With("name", name).Info("New Prometheus metric registered")
|
||||
}
|
||||
|
||||
func Update(ims ...InstanceMetrics) {
|
||||
for _, im := range ims {
|
||||
func Update(iml InstanceMetricsList) {
|
||||
for _, im := range iml.Metrics {
|
||||
update(im)
|
||||
stats[im.HostName] = im.Stats
|
||||
}
|
||||
@@ -231,6 +230,10 @@ func update(im InstanceMetrics) {
|
||||
}
|
||||
}
|
||||
|
||||
type InstanceMetricsList struct {
|
||||
Metrics []InstanceMetrics `faker:"slice_len=5"`
|
||||
}
|
||||
|
||||
type InstanceMetrics struct {
|
||||
HostName string
|
||||
Status *model.ServerStatus
|
||||
@@ -249,18 +252,14 @@ func (os OverallStats) consolidate() OverallStats {
|
||||
return consolidated
|
||||
}
|
||||
|
||||
func safeMetric[T Number](v *T) float64 {
|
||||
func safeMetric[T int | float64 | float32](v *T) float64 {
|
||||
if v == nil {
|
||||
return 0
|
||||
}
|
||||
return float64(*v)
|
||||
}
|
||||
|
||||
type Number interface {
|
||||
constraints.Float | constraints.Integer
|
||||
}
|
||||
|
||||
func GetStats() OverallStats {
|
||||
func getStats() OverallStats {
|
||||
return stats.consolidate()
|
||||
}
|
||||
|
||||
|
||||
13
pkg/metrics/metrics_suite_test.go
Normal file
13
pkg/metrics/metrics_suite_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package metrics_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestSync(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Metrics Suite")
|
||||
}
|
||||
101
pkg/metrics/metrics_test.go
Normal file
101
pkg/metrics/metrics_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/bakito/adguardhome-sync/pkg/client/model"
|
||||
"github.com/go-faker/faker/v4"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
var _ = Describe("Metrics", func() {
|
||||
BeforeEach(func() {
|
||||
stats = make(OverallStats)
|
||||
})
|
||||
Context("Update / getStats", func() {
|
||||
It("generate correct stats", func() {
|
||||
Update(InstanceMetricsList{[]InstanceMetrics{
|
||||
{HostName: "foo", Status: &model.ServerStatus{}, Stats: &model.Stats{
|
||||
NumDnsQueries: ptr.To(100),
|
||||
DnsQueries: ptr.To(
|
||||
[]int{10, 20, 30, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
),
|
||||
}},
|
||||
{HostName: "bar", Status: &model.ServerStatus{}, Stats: &model.Stats{
|
||||
NumDnsQueries: ptr.To(200),
|
||||
DnsQueries: ptr.To(
|
||||
[]int{20, 40, 60, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
),
|
||||
}},
|
||||
{HostName: "aaa", Status: &model.ServerStatus{}, Stats: &model.Stats{
|
||||
NumDnsQueries: ptr.To(300),
|
||||
DnsQueries: ptr.To(
|
||||
[]int{30, 60, 90, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
),
|
||||
}},
|
||||
}})
|
||||
Ω(stats).Should(HaveKey("foo"))
|
||||
Ω(stats["foo"].NumDnsQueries).Should(Equal(ptr.To(100)))
|
||||
Ω(stats).Should(HaveKey("bar"))
|
||||
Ω(stats["bar"].NumDnsQueries).Should(Equal(ptr.To(200)))
|
||||
Ω(stats).Should(HaveKey("aaa"))
|
||||
Ω(stats["aaa"].NumDnsQueries).Should(Equal(ptr.To(300)))
|
||||
|
||||
os := getStats()
|
||||
tot := os.Total()
|
||||
Ω(*tot.NumDnsQueries).Should(Equal(600))
|
||||
Ω(
|
||||
*tot.DnsQueries,
|
||||
).Should(Equal([]int{60, 120, 180, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}))
|
||||
|
||||
foo := os["foo"]
|
||||
bar := os["bar"]
|
||||
aaa := os["aaa"]
|
||||
|
||||
Ω(*foo.NumDnsQueries).Should(Equal(100))
|
||||
Ω(*bar.NumDnsQueries).Should(Equal(200))
|
||||
Ω(*aaa.NumDnsQueries).Should(Equal(300))
|
||||
Ω(
|
||||
*foo.DnsQueries,
|
||||
).Should(Equal([]int{10, 20, 30, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}))
|
||||
Ω(
|
||||
*bar.DnsQueries,
|
||||
).Should(Equal([]int{20, 40, 60, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}))
|
||||
Ω(
|
||||
*aaa.DnsQueries,
|
||||
).Should(Equal([]int{30, 60, 90, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}))
|
||||
})
|
||||
})
|
||||
Context("StatsGraph", func() {
|
||||
var metrics InstanceMetricsList
|
||||
BeforeEach(func() {
|
||||
err := faker.FakeData(&metrics)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
It("should provide correct results with faked values", func() {
|
||||
Update(metrics)
|
||||
|
||||
_, dns, blocked, malware, adult := StatsGraph()
|
||||
|
||||
verifyStats(dns)
|
||||
verifyStats(blocked)
|
||||
verifyStats(malware)
|
||||
verifyStats(adult)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func verifyStats(lines []line) {
|
||||
var total line
|
||||
sum := make([]int, len(lines[0].Data))
|
||||
for _, l := range lines {
|
||||
if l.Title == labelTotal {
|
||||
total = l
|
||||
} else {
|
||||
for i, d := range l.Data {
|
||||
sum[i] = sum[i] + d
|
||||
}
|
||||
}
|
||||
}
|
||||
Ω(sum).Should(Equal(total.Data))
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
package sync
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/client/model"
|
||||
"github.com/bakito/adguardhome-sync/pkg/metrics"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
const labelTotal = "Total"
|
||||
|
||||
var (
|
||||
blue = []int{78, 141, 245}
|
||||
blueAlternatives = [][]int{
|
||||
@@ -46,30 +47,43 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func statsGraph() (*model.Stats, []line, []line, []line, []line) {
|
||||
s := metrics.GetStats()
|
||||
func StatsGraph() (*model.Stats, []line, []line, []line, []line) {
|
||||
s := getStats()
|
||||
t := s.Total()
|
||||
dns := graphLines(t, s, blue, blueAlternatives, func(s *model.Stats) []int {
|
||||
return *s.DnsQueries
|
||||
return safeStats(s.DnsQueries)
|
||||
})
|
||||
blocked := graphLines(t, s, red, redAlternatives, func(s *model.Stats) []int {
|
||||
return *s.BlockedFiltering
|
||||
return safeStats(s.BlockedFiltering)
|
||||
})
|
||||
malware := graphLines(t, s, green, greenAlternatives, func(s *model.Stats) []int {
|
||||
return *s.ReplacedSafebrowsing
|
||||
return safeStats(s.ReplacedSafebrowsing)
|
||||
})
|
||||
adult := graphLines(t, s, yellow, yellowAlternatives, func(s *model.Stats) []int {
|
||||
return *s.ReplacedParental
|
||||
return safeStats(s.ReplacedParental)
|
||||
})
|
||||
|
||||
return t, dns, blocked, malware, adult
|
||||
}
|
||||
|
||||
func graphLines(t *model.Stats, s metrics.OverallStats, baseColor []int, altColors [][]int, dataCB func(s *model.Stats) []int) []line {
|
||||
func safeStats(stats *[]int) []int {
|
||||
if stats == nil {
|
||||
return make([]int, 0)
|
||||
}
|
||||
return *stats
|
||||
}
|
||||
|
||||
func graphLines(
|
||||
t *model.Stats,
|
||||
s OverallStats,
|
||||
baseColor []int,
|
||||
altColors [][]int,
|
||||
dataCB func(s *model.Stats) []int,
|
||||
) []line {
|
||||
g := &graph{
|
||||
total: line{
|
||||
Fill: true,
|
||||
Title: "Total",
|
||||
Title: labelTotal,
|
||||
Data: dataCB(t),
|
||||
R: baseColor[0],
|
||||
G: baseColor[1],
|
||||
@@ -79,7 +93,7 @@ func graphLines(t *model.Stats, s metrics.OverallStats, baseColor []int, altColo
|
||||
|
||||
var i int
|
||||
for name, data := range s {
|
||||
if name != metrics.StatsTotal {
|
||||
if name != StatsTotal {
|
||||
g.replicas = append(g.replicas, line{
|
||||
Fill: false,
|
||||
Title: name,
|
||||
@@ -105,7 +105,8 @@ var (
|
||||
return ac.client.SetCustomRules(ac.origin.filters.UserRules)
|
||||
}
|
||||
|
||||
if !utils.PtrEquals(ac.origin.filters.Enabled, rf.Enabled) || !utils.PtrEquals(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
|
||||
@@ -236,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 {
|
||||
|
||||
@@ -33,7 +33,7 @@ func (w *worker) handleSync(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (w *worker) handleRoot(c *gin.Context) {
|
||||
total, dns, blocked, malware, adult := statsGraph()
|
||||
total, dns, blocked, malware, adult := metrics.StatsGraph()
|
||||
|
||||
c.HTML(http.StatusOK, "index.html", map[string]interface{}{
|
||||
"DarkMode": w.cfg.API.DarkMode,
|
||||
@@ -42,14 +42,23 @@ func (w *worker) handleRoot(c *gin.Context) {
|
||||
"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)),
|
||||
"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,
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<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 () {
|
||||
@@ -47,7 +47,6 @@
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
<style>
|
||||
.stat-card {
|
||||
background-color: rgb(43, 43, 43);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
|
||||
padding: 15px;
|
||||
@@ -58,11 +57,6 @@
|
||||
height: 100%;
|
||||
}
|
||||
{{- if .Metrics }}
|
||||
body {
|
||||
background-color: rgb(30, 30, 30);
|
||||
color: rgb(255, 255, 255);
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
.stat-card h3 {
|
||||
margin: 0;
|
||||
font-size: 2rem;
|
||||
@@ -70,7 +64,6 @@
|
||||
.stat-card p {
|
||||
margin: 5px 0;
|
||||
font-size: 0.9rem;
|
||||
color: rgb(78, 141, 245);
|
||||
}
|
||||
.percentage {
|
||||
font-size: 0.9rem;
|
||||
@@ -142,7 +135,7 @@
|
||||
<div class="btn-group" role="group">
|
||||
<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-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<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>
|
||||
@@ -171,14 +164,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js"
|
||||
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous">
|
||||
<!-- 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@4.0.0/dist/js/bootstrap.min.js"
|
||||
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous">
|
||||
<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"></script>
|
||||
<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) {
|
||||
|
||||
@@ -26,13 +26,13 @@ func (w *worker) startScraping() {
|
||||
}
|
||||
|
||||
func (w *worker) scrape() {
|
||||
var ims []metrics.InstanceMetrics
|
||||
var iml metrics.InstanceMetricsList
|
||||
|
||||
ims = append(ims, w.getMetrics(w.cfg.Origin))
|
||||
iml.Metrics = append(iml.Metrics, w.getMetrics(w.cfg.Origin))
|
||||
for _, replica := range w.cfg.Replicas {
|
||||
ims = append(ims, w.getMetrics(replica))
|
||||
iml.Metrics = append(iml.Metrics, w.getMetrics(replica))
|
||||
}
|
||||
metrics.Update(ims...)
|
||||
metrics.Update(iml)
|
||||
}
|
||||
|
||||
func (w *worker) getMetrics(inst types.AdGuardInstance) (im metrics.InstanceMetrics) {
|
||||
|
||||
@@ -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
|
||||
@@ -167,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
|
||||
}
|
||||
|
||||
@@ -275,12 +281,14 @@ 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{
|
||||
@@ -303,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) {
|
||||
|
||||
@@ -273,14 +273,18 @@ var _ = Describe("Sync", func() {
|
||||
})
|
||||
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())
|
||||
@@ -315,7 +319,8 @@ var _ = Describe("Sync", func() {
|
||||
var interval model.QueryLogConfigInterval = 123
|
||||
ac.origin.queryLogConfig.Interval = &interval
|
||||
cl.EXPECT().QueryLogConfig().Return(qlc, nil)
|
||||
cl.EXPECT().SetQueryLogConfig(&model.QueryLogConfigWithIgnored{QueryLogConfig: 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())
|
||||
})
|
||||
@@ -434,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})
|
||||
@@ -447,16 +454,22 @@ var _ = Describe("Sync", func() {
|
||||
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.cfg.ContinueOnError = true
|
||||
ac.origin.filters.Filters = utils.Ptr([]model.Filter{{Name: "foo", Url: "https://foo.bar"}, {Name: "bar", Url: "https://bar.foo"}})
|
||||
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)
|
||||
|
||||
@@ -27,15 +27,15 @@ func NewFeatures(enabled bool) Features {
|
||||
|
||||
// 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"`
|
||||
Theme bool `json:"theme" yaml:"theme" env:"FEATURES_THEME"`
|
||||
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
|
||||
@@ -46,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
|
||||
|
||||
@@ -18,39 +18,39 @@ 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"`
|
||||
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 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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
KeyName string `json:"keyName,omitempty" yaml:"keyName,omitempty" env:"API_TLS_KEY_NAME"`
|
||||
}
|
||||
|
||||
func (t TLS) Enabled() bool {
|
||||
@@ -140,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:"-"`
|
||||
@@ -187,8 +187,8 @@ func (i *AdGuardInstance) Init() error {
|
||||
}
|
||||
|
||||
func mask(s string) string {
|
||||
if s == "" {
|
||||
return "***"
|
||||
if len(s) < 3 {
|
||||
return strings.Repeat("*", len(s))
|
||||
}
|
||||
mask := strings.Repeat("*", len(s)-2)
|
||||
return fmt.Sprintf("%v%s%v", string(s[0]), mask, string(s[len(s)-1]))
|
||||
|
||||
15
pkg/types/types_fuzz_test.go
Normal file
15
pkg/types/types_fuzz_test.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func FuzzMask(f *testing.F) {
|
||||
testcases := []string{"", "a", "ab", "abc", "abcd"}
|
||||
for _, tc := range testcases {
|
||||
f.Add(tc)
|
||||
}
|
||||
f.Fuzz(func(t *testing.T, value string) {
|
||||
_ = mask(value)
|
||||
})
|
||||
}
|
||||
@@ -83,6 +83,15 @@ var _ = Describe("Types", func() {
|
||||
Ω(masked.API.Password).Should(Equal("p**s"))
|
||||
})
|
||||
})
|
||||
DescribeTable("mask should work correctly",
|
||||
func(value, expected string) {
|
||||
Ω(mask(value)).Should(Equal(expected))
|
||||
},
|
||||
Entry(`Empty password`, "", ""),
|
||||
Entry(`1 char password`, "a", "*"),
|
||||
Entry(`2 char password`, "ab", "**"),
|
||||
Entry(`3 char password`, "abc", "a*c"),
|
||||
)
|
||||
})
|
||||
Context("Feature", func() {
|
||||
Context("LogDisabled", func() {
|
||||
|
||||
31
testdata/config/print-config_test_expected1.md
vendored
Normal file
31
testdata/config/print-config_test_expected1.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<!-- PLEASE COPY THE FOLLOWING OUTPUT AS IS INTO THE GITHUB ISSUE (Don't forget to mask your usernames, passwords, IPs and other sensitive information when using this in an issue ) -->
|
||||
|
||||
### Runtime
|
||||
|
||||
AdguardHome-Sync Version: %s
|
||||
Build: %s
|
||||
OperatingSystem: %s
|
||||
Architecture: %s
|
||||
OriginVersion: v0.0.1
|
||||
ReplicaVersions:
|
||||
- Replica 1: v0.0.2
|
||||
|
||||
### AdGuardHome sync aggregated config
|
||||
|
||||
```yaml
|
||||
origin:
|
||||
url: https://ha.xxxx.net:3000
|
||||
webURL: ""
|
||||
insecureSkipVerify: false
|
||||
autoSetup: false
|
||||
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```ini
|
||||
BAR=bar
|
||||
FOO=foo
|
||||
```
|
||||
|
||||
<!-- END OF GITHUB ISSUE CONTENT -->
|
||||
41
testdata/config/print-config_test_expected2.md
vendored
Normal file
41
testdata/config/print-config_test_expected2.md
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<!-- PLEASE COPY THE FOLLOWING OUTPUT AS IS INTO THE GITHUB ISSUE (Don't forget to mask your usernames, passwords, IPs and other sensitive information when using this in an issue ) -->
|
||||
|
||||
### Runtime
|
||||
|
||||
AdguardHome-Sync Version: %s
|
||||
Build: %s
|
||||
OperatingSystem: %s
|
||||
Architecture: %s
|
||||
OriginVersion: v0.0.1
|
||||
ReplicaVersions:
|
||||
- Replica 1: v0.0.2
|
||||
|
||||
### AdGuardHome sync aggregated config
|
||||
|
||||
```yaml
|
||||
origin:
|
||||
url: https://ha.xxxx.net:3000
|
||||
webURL: ""
|
||||
insecureSkipVerify: false
|
||||
autoSetup: false
|
||||
|
||||
```
|
||||
### AdGuardHome sync unmodified config file
|
||||
|
||||
Config file path: config.yaml
|
||||
|
||||
```yaml
|
||||
|
||||
origin:
|
||||
url: https://ha.xxxx.net:3000
|
||||
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```ini
|
||||
BAR=bar
|
||||
FOO=foo
|
||||
```
|
||||
|
||||
<!-- END OF GITHUB ISSUE CONTENT -->
|
||||
Reference in New Issue
Block a user