Compare commits

...

17 Commits

Author SHA1 Message Date
bakito
64463b6842 add multi replica env support #4 2021-04-05 21:07:28 +02:00
bakito
9450c09e2a add docker build #4 2021-04-05 20:13:13 +02:00
bakito
514b228739 add prerequisites 2021-04-05 13:33:21 +02:00
bakito
06f95de085 ignore last updated timestamp / add testcase #3 2021-04-05 13:20:11 +02:00
bakito
1986e87518 correct log fields 2021-04-03 21:04:50 +02:00
bakito
cf0a381f80 log fields 2021-04-03 20:38:41 +02:00
bakito
5e591e04c3 simplifiy
cleanup code
skip arm for darwin and windows
2021-04-03 20:19:34 +02:00
bakito
fcf25538c0 make log available in api 2021-04-03 18:41:28 +02:00
bakito
aa95031136 sync stats and query log config 2021-04-03 17:52:08 +02:00
bakito
6fb2dd12a8 move log attribute to message 2021-03-31 02:34:26 +02:00
bakito
8db9c98644 synch toggles 2021-03-31 02:28:18 +02:00
Marc Brugger
f8578e85b2 add API server to trigger sync remotely 2021-03-29 08:43:18 +02:00
bakito
5dafdf5472 change output paths 2021-03-28 20:28:58 +02:00
bakito
23d5f30eb8 add systemd script 2021-03-28 20:25:25 +02:00
bakito
44609a93e3 correct logger name 2021-03-28 20:00:22 +02:00
bakito
70e60bb7d0 use external semver 2021-03-28 19:31:11 +02:00
bakito
aacffb1518 document config file 2021-03-28 17:04:41 +02:00
21 changed files with 971 additions and 169 deletions

44
.github/workflows/publish.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
name: quay
on:
push:
branches: main
release:
types:
- published
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Quay
uses: docker/login-action@v1
with:
registry: quay.io
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build and push ${{github.event.release.tag_name }}
id: docker_build_release
uses: docker/build-push-action@v2
if: ${{ github.event.release.tag_name != '' }}
with:
push: true
tags: quay.io/bakito/adguardhome-sync:latest,quay.io/bakito/adguardhome-sync:${{ github.event.release.tag_name }}
platforms: linux/amd64,linux/arm64,linux/arm/v7
build-args: VERSION=${{ github.event.release.tag_name }}
- name: Build and push main
id: docker_build_main
uses: docker/build-push-action@v2
if: ${{ github.event.release.tag_name == '' }}
with:
push: true
tags: quay.io/bakito/adguardhome-sync:main
platforms: linux/amd64,linux/arm64,linux/arm/v7
build-args: VERSION=main
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}

3
.gitignore vendored
View File

@@ -1,3 +1,6 @@
.idea .idea
coverage.out coverage.out
dist dist
adguardhome-sync
main
.adguardhome-sync.yaml

View File

@@ -1,29 +1,38 @@
# This is an example goreleaser.yaml file with some sane defaults. # This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com # Make sure to check the documentation at http://goreleaser.com
builds: builds:
- env: - env:
- CGO_ENABLED=0 - CGO_ENABLED=0
ldflags: ldflags:
- -s -w -X github.com/bakito/adguardhome-sync/version.Version={{.Version}} - -s -w -X github.com/bakito/adguardhome-sync/version.Version={{.Version}}
goos: goos:
- linux - linux
- windows - windows
- darwin - darwin
goarch: goarch:
- 386 - 386
- amd64 - amd64
- arm - arm
- arm64 - arm64
goarm: goarm:
- 5 - 5
- 6 - 6
- 7 - 7
hooks: ignore:
post: upx {{ .Path }} - goos: darwin
goarch: arm
- goos: darwin
goarch: arm64
- goos: windows
goarch: arm
- goos: windows
goarch: arm64
hooks:
post: upx {{ .Path }}
archives: archives:
- replacements: - replacements:
386: i386 386: i386
amd64: x86_64 amd64: x86_64
checksum: checksum:
name_template: 'checksums.txt' name_template: 'checksums.txt'
snapshot: snapshot:
@@ -32,5 +41,5 @@ changelog:
sort: asc sort: asc
filters: filters:
exclude: exclude:
- '^docs:' - '^docs:'
- '^test:' - '^test:'

27
Dockerfile Normal file
View File

@@ -0,0 +1,27 @@
FROM docker.io/library/golang:1.16 as builder
WORKDIR /go/src/app
RUN apt-get update && apt-get install -y upx
ARG VERSION=main
ENV GOPROXY=https://goproxy.io \
GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux
ADD . /go/src/app/
RUN go build -a -installsuffix cgo -ldflags="-w -s -X github.com/bakito/adguardhome-sync/version.Version=${VERSION}" -o adguardhome-sync . \
&& upx -q adguardhome-sync
# application image
FROM scratch
WORKDIR /opt/go
LABEL maintainer="bakito <github@bakito.ch>"
EXPOSE 8080
ENTRYPOINT ["/opt/go/adguardhome-sync"]
CMD ["run", "--config", "/config/adguardhome-sync.yaml"]
COPY --from=builder /go/src/app/adguardhome-sync /opt/go/adguardhome-sync
USER 1001

View File

@@ -20,10 +20,15 @@ test: tidy fmt vet
go test ./... -coverprofile=coverage.out go test ./... -coverprofile=coverage.out
go tool cover -func=coverage.out go tool cover -func=coverage.out
release: release: semver
@version=$$(go run version/semver/main.go); \ @version=$$(semver); \
git tag -s $$version -m"Release $$version" git tag -s $$version -m"Release $$version"
goreleaser --rm-dist goreleaser --rm-dist
test-release: test-release:
goreleaser --skip-publish --snapshot --rm-dist goreleaser --skip-publish --snapshot --rm-dist
semver:
ifeq (, $(shell which semver))
$(shell go get -u github.com/bakito/semver)
endif

104
README.md
View File

@@ -6,6 +6,7 @@ Synchronize [AdGuardHome](https://github.com/AdguardTeam/AdGuardHome) config to
## Current sync features ## Current sync features
- General Settings
- Filters - Filters
- Rewrites - Rewrites
- Services - Services
@@ -17,6 +18,10 @@ Synchronize [AdGuardHome](https://github.com/AdguardTeam/AdGuardHome) config to
go get -u github.com/bakito/adguardhome-sync go get -u github.com/bakito/adguardhome-sync
``` ```
## Prerequisites
Both the origin and replica mist be initially setup via the Adguard Home installation wizard.
## Run ## Run
```bash ```bash
@@ -33,4 +38,101 @@ adguardhome-sync run
# run as daemon # run as daemon
adguardhome-sync run --cron "*/10 * * * *" adguardhome-sync run --cron "*/10 * * * *"
``` ```
## docker cli
```bash
docker run -d \
--name=adguardhome-sync \
-p 8080:8080 \
-v /path/to/appdata/config/adguardhome-sync.yaml:/config/adguardhome-sync.yaml \
--restart unless-stopped \
quay.io/bakito/adguardhome-sync:latest
```
## docker compose
### config file
```yaml
---
version: "2.1"
services:
adguardhome-sync:
image: quay.io/bakito/adguardhome-sync
container_name: adguardhome-sync
volumes:
- /path/to/appdata/config/adguardhome-sync.yaml:/config/adguardhome-sync.yaml
ports:
- 8080:8080
restart: unless-stopped
```
### env
```yaml
---
version: "2.1"
services:
adguardhome-sync:
image: quay.io/bakito/adguardhome-sync
container_name: adguardhome-sync
environment:
- ORIGIN_URL=https://192.168.1.2:3000
- ORIGIN_USERNAME=username
- ORIGIN_PASSWORD=password
- REPLICA_URL=http://192.168.1.3
- REPLICA_USERNAME=username
- REPLICA_PASSWORD=password
- REPLICA1_URL=http://192.168.1.4
- REPLICA1_USERNAME=username
- REPLICA1_PASSWORD=password
- REPLICA1_APIPATH=/some/path/control
- CRON=*/10 * * * * # run every 10 minutes
ports:
- 8080:8080
restart: unless-stopped
```
### Config file
location: $HOME/.adguardhome-sync.yaml
```yaml
# cron expression to run in daemon mode. (default; "" = runs only once)
cron: "*/10 * * * *"
origin:
# url of the origin instance
url: https://192.168.1.2:3000
# apiPath: define an api path if other than "/control"
# insecureSkipVerify: true # disable tls check
username: username
password: password
# replica instance (optional, if only one)
replica:
# url of the replica instance
url: http://192.168.1.3
username: username
password: password
# replicas instances (optional, if more than one)
replicas:
# url of the replica instance
- url: http://192.168.1.3
username: username
password: password
- url: http://192.168.1.4
username: username
password: password
# Configure the sync API server, disabled if api port is 0
api:
# Port, default 8080
port: 8080
# if username and password are defined, basic auth is applied to the sync API
username: username
password: password
```

View File

@@ -3,19 +3,23 @@ package cmd
import ( import (
"fmt" "fmt"
"os" "os"
"regexp"
"strings" "strings"
"github.com/bakito/adguardhome-sync/pkg/log" "github.com/bakito/adguardhome-sync/pkg/log"
"github.com/bakito/adguardhome-sync/pkg/types"
"github.com/spf13/cobra"
homedir "github.com/mitchellh/go-homedir" homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
const ( const (
configCron = "cron" configCron = "cron"
configAPIPort = "api.port"
configAPIUsername = "api.username"
configAPIPassword = "api.password"
configOriginURL = "origin.url" configOriginURL = "origin.url"
configOriginAPIPath = "origin.apiPath" configOriginAPIPath = "origin.apiPath"
configOriginUsername = "origin.username" configOriginUsername = "origin.username"
@@ -27,11 +31,17 @@ const (
configReplicaUsername = "replica.username" configReplicaUsername = "replica.username"
configReplicaPassword = "replica.password" configReplicaPassword = "replica.password"
configReplicaInsecureSkipVerify = "replica.insecureSkipVerify" configReplicaInsecureSkipVerify = "replica.insecureSkipVerify"
envReplicasUsernameFormat = "REPLICA%s_USERNAME"
envReplicasPasswordFormat = "REPLICA%s_PASSWORD"
envReplicasAPIPathFormat = "REPLICA%s_APIPATH"
envReplicasInsecureSkipVerifyFormat = "REPLICA%s_INSECURESKIPVERIFY"
) )
var ( var (
cfgFile string cfgFile string
logger = log.GetLogger("root") logger = log.GetLogger("root")
envReplicasURLPattern = regexp.MustCompile(`^REPLICA(\d+)_URL=(.*)`)
) )
// rootCmd represents the base command when called without any subcommands // rootCmd represents the base command when called without any subcommands
@@ -79,13 +89,47 @@ func initConfig() {
// Search config in home directory with name ".adguardhome-sync" (without extension). // Search config in home directory with name ".adguardhome-sync" (without extension).
viper.AddConfigPath(home) viper.AddConfigPath(home)
viper.SetConfigName(".adguardhome-sync") viper.SetConfigName(".adguardhome-sync")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_"))
} }
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_"))
viper.AutomaticEnv() // read in environment variables that match viper.AutomaticEnv() // read in environment variables that match
// If a config file is found, read it in. // If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil { if err := viper.ReadInConfig(); err == nil {
logger.Info("Using config file:", viper.ConfigFileUsed()) logger.Info("Using config file:", viper.ConfigFileUsed())
} else if cfgFile != "" {
fmt.Println(err)
os.Exit(1)
} }
} }
func getConfig() (*types.Config, error) {
cfg := &types.Config{}
if err := viper.Unmarshal(cfg); err != nil {
return nil, err
}
if len(cfg.Replicas) == 0 {
cfg.Replicas = append(cfg.Replicas, collectEnvReplicas()...)
}
return cfg, nil
}
// Manually collect replicas from env.
func collectEnvReplicas() []types.AdGuardInstance {
var replicas []types.AdGuardInstance
for _, v := range os.Environ() {
if envReplicasURLPattern.MatchString(v) {
sm := envReplicasURLPattern.FindStringSubmatch(v)
re := types.AdGuardInstance{
URL: sm[2],
Username: os.Getenv(fmt.Sprintf(envReplicasUsernameFormat, sm[1])),
Password: os.Getenv(fmt.Sprintf(envReplicasPasswordFormat, sm[1])),
APIPath: os.Getenv(fmt.Sprintf(envReplicasAPIPathFormat, sm[1])),
InsecureSkipVerify: strings.EqualFold(os.Getenv(fmt.Sprintf(envReplicasInsecureSkipVerifyFormat, sm[1])), "true"),
}
replicas = append(replicas, re)
}
}
return replicas
}

View File

@@ -3,10 +3,8 @@ package cmd
import ( import (
"github.com/bakito/adguardhome-sync/pkg/log" "github.com/bakito/adguardhome-sync/pkg/log"
"github.com/bakito/adguardhome-sync/pkg/sync" "github.com/bakito/adguardhome-sync/pkg/sync"
"github.com/bakito/adguardhome-sync/pkg/types"
"github.com/spf13/viper"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper"
) )
// runCmd represents the run command // runCmd represents the run command
@@ -14,16 +12,15 @@ var doCmd = &cobra.Command{
Use: "run", Use: "run",
Short: "Start a synchronisation from origin to replica", Short: "Start a synchronisation from origin to replica",
Long: `Synchronizes the configuration form an origin instance to a replica`, Long: `Synchronizes the configuration form an origin instance to a replica`,
Run: func(cmd *cobra.Command, args []string) { RunE: func(cmd *cobra.Command, args []string) error {
logger = log.GetLogger("run")
logger = log.GetLogger("root") cfg, err := getConfig()
cfg := &types.Config{} if err != nil {
if err := viper.Unmarshal(cfg); err != nil {
logger.Error(err) logger.Error(err)
return return err
} }
sync.Sync(cfg) return sync.Sync(cfg)
}, },
} }
@@ -31,6 +28,12 @@ func init() {
rootCmd.AddCommand(doCmd) rootCmd.AddCommand(doCmd)
doCmd.PersistentFlags().String("cron", "", "The cron expression to run in daemon mode") doCmd.PersistentFlags().String("cron", "", "The cron expression to run in daemon mode")
_ = viper.BindPFlag(configCron, doCmd.PersistentFlags().Lookup("cron")) _ = viper.BindPFlag(configCron, doCmd.PersistentFlags().Lookup("cron"))
doCmd.PersistentFlags().Int("api-port", 8080, "Sync API Port, the API endpoint will be started to enable remote triggering; if 0 port API is disabled.")
_ = viper.BindPFlag(configAPIPort, doCmd.PersistentFlags().Lookup("api-port"))
doCmd.PersistentFlags().String("api-username", "", "Sync API username")
_ = viper.BindPFlag(configAPIUsername, doCmd.PersistentFlags().Lookup("api-username"))
doCmd.PersistentFlags().String("api-password", "", "Sync API password")
_ = viper.BindPFlag(configAPIPassword, doCmd.PersistentFlags().Lookup("api-password"))
doCmd.PersistentFlags().String("origin-url", "", "Origin instance url") doCmd.PersistentFlags().String("origin-url", "", "Origin instance url")
_ = viper.BindPFlag(configOriginURL, doCmd.PersistentFlags().Lookup("origin-url")) _ = viper.BindPFlag(configOriginURL, doCmd.PersistentFlags().Lookup("origin-url"))

5
go.mod
View File

@@ -3,9 +3,10 @@ module github.com/bakito/adguardhome-sync
go 1.16 go 1.16
require ( require (
github.com/coreos/go-semver v0.3.0 github.com/go-resty/resty/v2 v2.4.0
github.com/go-resty/resty/v2 v2.5.0
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/onsi/ginkgo v1.16.0
github.com/onsi/gomega v1.11.0
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/spf13/cobra v1.1.3 github.com/spf13/cobra v1.1.3
github.com/spf13/viper v1.7.1 github.com/spf13/viper v1.7.1

72
go.sum
View File

@@ -28,7 +28,6 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
@@ -39,16 +38,18 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-resty/resty/v2 v2.5.0 h1:WFb5bD49/85PO7WgAjZ+/TJQ+Ty1XOcWEfD1zIFCM1c= github.com/go-resty/resty/v2 v2.4.0 h1:s6TItTLejEI+2mn98oijC5w/Rk2YU+OA6x0mnZN6r6k=
github.com/go-resty/resty/v2 v2.5.0/go.mod h1:B88+xCTEwvfD94NOuE6GS1wMlnoKNY8eEiNizfNwOwA= github.com/go-resty/resty/v2 v2.4.0/go.mod h1:B88+xCTEwvfD94NOuE6GS1wMlnoKNY8eEiNizfNwOwA=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -59,10 +60,20 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -96,6 +107,7 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@@ -132,7 +144,18 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.0 h1:NBrNLB37exjJLxXtFOktx6CISBdS1aF8+7MwKlTV8U4=
github.com/onsi/ginkgo v1.16.0/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug=
github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@@ -184,12 +207,14 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@@ -209,6 +234,8 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -228,8 +255,11 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -242,6 +272,9 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -253,9 +286,11 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -266,8 +301,14 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -294,9 +335,15 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -318,18 +365,29 @@ google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvx
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -16,6 +16,7 @@ var (
l = log.GetLogger("client") l = log.GetLogger("client")
) )
// New create a new client
func New(config types.AdGuardInstance) (Client, error) { func New(config types.AdGuardInstance) (Client, error) {
var apiURL string var apiURL string
@@ -46,10 +47,12 @@ func New(config types.AdGuardInstance) (Client, error) {
}, nil }, nil
} }
// Client AdGuard Home API client interface
type Client interface { type Client interface {
Host() string Host() string
Status() (*types.Status, error) Status() (*types.Status, error)
ToggleProtection(enable bool) error
RewriteList() (*types.RewriteEntries, error) RewriteList() (*types.RewriteEntries, error)
AddRewriteEntries(e ...types.RewriteEntry) error AddRewriteEntries(e ...types.RewriteEntry) error
DeleteRewriteEntries(e ...types.RewriteEntry) error DeleteRewriteEntries(e ...types.RewriteEntry) error
@@ -61,8 +64,11 @@ type Client interface {
RefreshFilters(whitelist bool) error RefreshFilters(whitelist bool) error
SetCustomRules(rules types.UserRules) error SetCustomRules(rules types.UserRules) error
ToggleSaveBrowsing(enable bool) error SafeBrowsing() (bool, error)
ToggleSafeBrowsing(enable bool) error
Parental() (bool, error)
ToggleParental(enable bool) error ToggleParental(enable bool) error
SafeSearch() (bool, error)
ToggleSafeSearch(enable bool) error ToggleSafeSearch(enable bool) error
Services() (*types.Services, error) Services() (*types.Services, error)
@@ -72,6 +78,11 @@ type Client interface {
AddClients(client ...types.Client) error AddClients(client ...types.Client) error
UpdateClients(client ...types.Client) error UpdateClients(client ...types.Client) error
DeleteClients(client ...types.Client) error DeleteClients(client ...types.Client) error
QueryLogConfig() (*types.QueryLogConfig, error)
SetQueryLogConfig(enabled bool, interval int, anonymizeClientIP bool) error
StatsConfig() (*types.IntervalConfig, error)
SetStatsConfig(interval int) error
} }
type client struct { type client struct {
@@ -118,20 +129,38 @@ func (cl *client) DeleteRewriteEntries(entries ...types.RewriteEntry) error {
return nil return nil
} }
func (cl *client) ToggleSaveBrowsing(enable bool) error { func (cl *client) SafeBrowsing() (bool, error) {
return cl.toggle("safebrowsing", enable) return cl.toggleStatus("safebrowsing")
}
func (cl *client) ToggleSafeBrowsing(enable bool) error {
return cl.toggleBool("safebrowsing", enable)
}
func (cl *client) Parental() (bool, error) {
return cl.toggleStatus("parental")
} }
func (cl *client) ToggleParental(enable bool) error { func (cl *client) ToggleParental(enable bool) error {
return cl.toggle("parental", enable) return cl.toggleBool("parental", enable)
}
func (cl *client) SafeSearch() (bool, error) {
return cl.toggleStatus("safesearch")
} }
func (cl *client) ToggleSafeSearch(enable bool) error { func (cl *client) ToggleSafeSearch(enable bool) error {
return cl.toggle("safesearch", enable) return cl.toggleBool("safesearch", enable)
} }
func (cl *client) toggle(mode string, enable bool) error { func (cl *client) toggleStatus(mode string) (bool, error) {
cl.log.With("mode", mode, "enable", enable).Info("Toggle") fs := &types.EnableConfig{}
_, err := cl.client.R().EnableTrace().SetResult(fs).Get(fmt.Sprintf("/%s/status", mode))
return fs.Enabled, err
}
func (cl *client) toggleBool(mode string, enable bool) error {
cl.log.With("enable", enable).Info(fmt.Sprintf("Toggle %s", mode))
var target string var target string
if enable { if enable {
target = "enable" target = "enable"
@@ -178,6 +207,12 @@ func (cl *client) RefreshFilters(whitelist bool) error {
return err return err
} }
func (cl *client) ToggleProtection(enable bool) error {
cl.log.With("enable", enable).Info("Toggle protection")
_, err := cl.client.R().EnableTrace().SetBody(&types.Protection{ProtectionEnabled: enable}).Post("/dns_config")
return err
}
func (cl *client) SetCustomRules(rules types.UserRules) error { func (cl *client) SetCustomRules(rules types.UserRules) error {
cl.log.With("rules", len(rules)).Info("Set user rules") cl.log.With("rules", len(rules)).Info("Set user rules")
_, err := cl.client.R().EnableTrace().SetBody(rules.String()).Post("/filtering/set_rules") _, err := cl.client.R().EnableTrace().SetBody(rules.String()).Post("/filtering/set_rules")
@@ -186,7 +221,10 @@ func (cl *client) SetCustomRules(rules types.UserRules) error {
func (cl *client) ToggleFiltering(enabled bool, interval int) error { func (cl *client) ToggleFiltering(enabled bool, interval int) error {
cl.log.With("enabled", enabled, "interval", interval).Info("Toggle filtering") cl.log.With("enabled", enabled, "interval", interval).Info("Toggle filtering")
_, err := cl.client.R().EnableTrace().SetBody(&types.FilteringConfig{Enabled: enabled, Interval: interval}).Post("/filtering/config") _, err := cl.client.R().EnableTrace().SetBody(&types.FilteringConfig{
EnableConfig: types.EnableConfig{Enabled: enabled},
IntervalConfig: types.IntervalConfig{Interval: interval},
}).Post("/filtering/config")
return err return err
} }
@@ -240,3 +278,31 @@ func (cl *client) DeleteClients(clients ...types.Client) error {
} }
return nil return nil
} }
func (cl *client) QueryLogConfig() (*types.QueryLogConfig, error) {
qlc := &types.QueryLogConfig{}
_, err := cl.client.R().EnableTrace().SetResult(qlc).Get("/querylog_info")
return qlc, err
}
func (cl *client) SetQueryLogConfig(enabled bool, interval int, anonymizeClientIP bool) error {
cl.log.With("enabled", enabled, "interval", interval, "anonymizeClientIP", anonymizeClientIP).Info("Set query log config")
_, err := cl.client.R().EnableTrace().SetBody(&types.QueryLogConfig{
EnableConfig: types.EnableConfig{Enabled: enabled},
IntervalConfig: types.IntervalConfig{Interval: interval},
AnonymizeClientIP: anonymizeClientIP,
}).Post("/querylog_config")
return err
}
func (cl *client) StatsConfig() (*types.IntervalConfig, error) {
stats := &types.IntervalConfig{}
_, err := cl.client.R().EnableTrace().SetResult(stats).Get("/stats_info")
return stats, err
}
func (cl *client) SetStatsConfig(interval int) error {
cl.log.With("interval", interval).Info("Set stats config")
_, err := cl.client.R().EnableTrace().SetBody(&types.IntervalConfig{Interval: interval}).Post("/stats_config")
return err
}

View File

@@ -2,9 +2,15 @@ package log
import ( import (
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
) )
var rootLogger *zap.Logger const logHistorySize = 50
var (
rootLogger *zap.Logger
logs []string
)
// GetLogger returns a named logger // GetLogger returns a named logger
func GetLogger(name string) *zap.SugaredLogger { func GetLogger(name string) *zap.SugaredLogger {
@@ -19,10 +25,68 @@ func init() {
Development: false, Development: false,
Encoding: "console", Encoding: "console",
EncoderConfig: zap.NewDevelopmentEncoderConfig(), EncoderConfig: zap.NewDevelopmentEncoderConfig(),
OutputPaths: []string{"stderr"}, OutputPaths: []string{"stdout"},
ErrorOutputPaths: []string{"stderr"}, ErrorOutputPaths: []string{"stderr"},
} }
opt := zap.WrapCore(func(c zapcore.Core) zapcore.Core {
return zapcore.NewTee(c, &logList{
enc: zapcore.NewConsoleEncoder(cfg.EncoderConfig),
LevelEnabler: cfg.Level,
})
})
rootLogger, _ = cfg.Build() rootLogger, _ = cfg.Build(opt)
rootLogger.Sugar() }
type logList struct {
zapcore.LevelEnabler
enc zapcore.Encoder
}
func (l *logList) clone() *logList {
return &logList{
LevelEnabler: l.LevelEnabler,
enc: l.enc.Clone(),
}
}
func (l *logList) With(fields []zapcore.Field) zapcore.Core {
clone := l.clone()
addFields(clone.enc, fields)
return clone
}
func (l *logList) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if l.Enabled(ent.Level) {
return ce.AddCore(ent, l)
}
return ce
}
func (l *logList) Write(ent zapcore.Entry, fields []zapcore.Field) error {
buf, err := l.enc.EncodeEntry(ent, fields)
if err != nil {
return err
}
logs = append(logs, buf.String())
if len(logs) > logHistorySize {
logs = logs[len(logs)-logHistorySize:]
}
return nil
}
func (l *logList) Sync() error {
return nil
}
// Logs get the current logs
func Logs() []string {
return logs
}
func addFields(enc zapcore.ObjectEncoder, fields []zapcore.Field) {
for i := range fields {
fields[i].AddTo(enc)
}
} }

126
pkg/sync/http.go Normal file
View File

@@ -0,0 +1,126 @@
package sync
import (
"context"
"fmt"
"net"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/bakito/adguardhome-sync/pkg/log"
"github.com/bakito/adguardhome-sync/version"
)
func (w *worker) handleSync(rw http.ResponseWriter, req *http.Request) {
switch req.Method {
case http.MethodPost:
l.With("remote-addr", req.RemoteAddr).Info("Starting sync from API")
w.sync()
default:
http.Error(rw, "only POST allowed", http.StatusBadRequest)
}
}
func (w *worker) handleRoot(rw http.ResponseWriter, _ *http.Request) {
_, _ = rw.Write([]byte("adguardhome-sync"))
}
func (w *worker) handleLogs(rw http.ResponseWriter, _ *http.Request) {
_, _ = rw.Write([]byte(strings.Join(log.Logs(), "")))
}
func (w *worker) basicAuth(h http.HandlerFunc) http.HandlerFunc {
return func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
username, password, authOK := r.BasicAuth()
if !authOK {
http.Error(rw, "Not authorized", 401)
return
}
if username != w.cfg.API.Username || password != w.cfg.API.Password {
http.Error(rw, "Not authorized", 401)
return
}
h.ServeHTTP(rw, r)
}
}
func use(h http.HandlerFunc, middleware ...func(http.HandlerFunc) http.HandlerFunc) http.HandlerFunc {
for _, m := range middleware {
h = m(h)
}
return h
}
func (w *worker) listenAndServe() {
l.With("version", version.Version, "port", w.cfg.API.Port).Info("Starting API server")
ctx, cancel := context.WithCancel(context.Background())
mux := http.NewServeMux()
httpServer := &http.Server{
Addr: fmt.Sprintf(":%d", w.cfg.API.Port),
Handler: mux,
BaseContext: func(_ net.Listener) context.Context { return ctx },
}
var mw []func(http.HandlerFunc) http.HandlerFunc
if w.cfg.API.Username != "" && w.cfg.API.Password != "" {
mw = append(mw, w.basicAuth)
}
mux.HandleFunc("/api/v1/sync", use(w.handleSync, mw...))
mux.HandleFunc("/api/v1/logs", use(w.handleLogs, mw...))
mux.HandleFunc("/", use(w.handleRoot, mw...))
go func() {
if err := httpServer.ListenAndServe(); err != http.ErrServerClosed {
l.With("error", err).Fatalf("HTTP server ListenAndServe")
}
}()
signalChan := make(chan os.Signal, 1)
signal.Notify(
signalChan,
syscall.SIGHUP, // kill -SIGHUP XXXX
syscall.SIGINT, // kill -SIGINT XXXX or Ctrl+c
syscall.SIGQUIT, // kill -SIGQUIT XXXX
)
<-signalChan
l.Info("os.Interrupt - shutting down...")
go func() {
<-signalChan
l.Fatal("os.Kill - terminating...")
}()
gracefullCtx, cancelShutdown := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelShutdown()
if w.cron != nil {
l.Info("Stopping cron")
w.cron.Stop()
}
if err := httpServer.Shutdown(gracefullCtx); err != nil {
l.With("error", err).Error("Shutdown error")
defer os.Exit(1)
} else {
l.Info("API server stopped")
}
// manually cancel context if not using httpServer.RegisterOnShutdown(cancel)
cancel()
defer os.Exit(0)
}

View File

@@ -1,9 +1,12 @@
package sync package sync
import ( import (
"fmt"
"github.com/bakito/adguardhome-sync/pkg/client" "github.com/bakito/adguardhome-sync/pkg/client"
"github.com/bakito/adguardhome-sync/pkg/log" "github.com/bakito/adguardhome-sync/pkg/log"
"github.com/bakito/adguardhome-sync/pkg/types" "github.com/bakito/adguardhome-sync/pkg/types"
"github.com/bakito/adguardhome-sync/version"
"github.com/robfig/cron/v3" "github.com/robfig/cron/v3"
"go.uber.org/zap" "go.uber.org/zap"
) )
@@ -13,28 +16,61 @@ var (
) )
// Sync config from origin to replica // Sync config from origin to replica
func Sync(cfg *types.Config) { func Sync(cfg *types.Config) error {
if cfg.Origin.URL == "" {
return fmt.Errorf("origin URL is required")
}
if len(cfg.UniqueReplicas()) == 0 {
return fmt.Errorf("no replicas configured")
}
w := &worker{
cfg: cfg,
}
if cfg.Cron != "" { if cfg.Cron != "" {
c := cron.New() w.cron = cron.New()
cl := l.With("cron", cfg.Cron) cl := l.With("version", version.Version, "cron", cfg.Cron)
_, err := c.AddFunc(cfg.Cron, func() { _, err := w.cron.AddFunc(cfg.Cron, func() {
sync(cfg) w.sync()
}) })
if err != nil { if err != nil {
cl.With("error", err).Error("Error creating cron job") cl.With("error", err).Error("Error during cron job setup")
return return err
}
cl.Info("Setup cronjob")
if cfg.API.Port != 0 {
w.cron.Start()
} else {
w.cron.Run()
} }
cl.Info("Starting cronjob")
c.Run()
} else { } else {
sync(cfg) w.sync()
} }
if cfg.API.Port != 0 {
w.listenAndServe()
}
return nil
} }
func sync(cfg *types.Config) { type worker struct {
oc, err := client.New(cfg.Origin) cfg *types.Config
running bool
cron *cron.Cron
}
func (w *worker) sync() {
if w.running {
l.Info("Sync already running")
return
}
w.running = true
defer func() { w.running = false }()
oc, err := client.New(w.cfg.Origin)
if err != nil { if err != nil {
l.With("error", err, "url", cfg.Origin.URL).Error("Error creating origin client") l.With("error", err, "url", w.cfg.Origin.URL).Error("Error creating origin client")
return return
} }
@@ -48,6 +84,22 @@ func sync(cfg *types.Config) {
return return
} }
o.parental, err = oc.Parental()
if err != nil {
sl.With("error", err).Error("Error getting parental status")
return
}
o.safeSearch, err = oc.SafeSearch()
if err != nil {
sl.With("error", err).Error("Error getting safe search status")
return
}
o.safeBrowsing, err = oc.SafeBrowsing()
if err != nil {
sl.With("error", err).Error("Error getting safe browsing status")
return
}
o.rewrites, err = oc.RewriteList() o.rewrites, err = oc.RewriteList()
if err != nil { if err != nil {
sl.With("error", err).Error("Error getting origin rewrites") sl.With("error", err).Error("Error getting origin rewrites")
@@ -70,14 +122,24 @@ func sync(cfg *types.Config) {
sl.With("error", err).Error("Error getting origin clients") sl.With("error", err).Error("Error getting origin clients")
return return
} }
o.queryLogConfig, err = oc.QueryLogConfig()
if err != nil {
sl.With("error", err).Error("Error getting query log config")
return
}
o.statsConfig, err = oc.StatsConfig()
if err != nil {
sl.With("error", err).Error("Error getting stats config")
return
}
replicas := cfg.UniqueReplicas() replicas := w.cfg.UniqueReplicas()
for _, replica := range replicas { for _, replica := range replicas {
syncTo(sl, o, replica) w.syncTo(sl, o, replica)
} }
} }
func syncTo(l *zap.SugaredLogger, o *origin, replica types.AdGuardInstance) { func (w *worker) syncTo(l *zap.SugaredLogger, o *origin, replica types.AdGuardInstance) {
rc, err := client.New(replica) rc, err := client.New(replica)
if err != nil { if err != nil {
@@ -97,24 +159,36 @@ func syncTo(l *zap.SugaredLogger, o *origin, replica types.AdGuardInstance) {
l.With("originVersion", o.status.Version, "replicaVersion", rs.Version).Warn("Versions do not match") l.With("originVersion", o.status.Version, "replicaVersion", rs.Version).Warn("Versions do not match")
} }
err = syncRewrites(o.rewrites, rc) err = w.syncGeneralSettings(o, rs, rc)
if err != nil {
l.With("error", err).Error("Error syncing general settings")
return
}
err = w.syncConfigs(o, rs, rc)
if err != nil {
l.With("error", err).Error("Error syncing configs")
return
}
err = w.syncRewrites(o.rewrites, rc)
if err != nil { if err != nil {
l.With("error", err).Error("Error syncing rewrites") l.With("error", err).Error("Error syncing rewrites")
return return
} }
err = syncFilters(o.filters, rc) err = w.syncFilters(o.filters, rc)
if err != nil { if err != nil {
l.With("error", err).Error("Error syncing filters") l.With("error", err).Error("Error syncing filters")
return return
} }
err = syncServices(o.services, rc) err = w.syncServices(o.services, rc)
if err != nil { if err != nil {
l.With("error", err).Error("Error syncing services") l.With("error", err).Error("Error syncing services")
return return
} }
if err = syncClients(o.clients, rc); err != nil { if err = w.syncClients(o.clients, rc); err != nil {
l.With("error", err).Error("Error syncing clients") l.With("error", err).Error("Error syncing clients")
return return
} }
@@ -122,7 +196,7 @@ func syncTo(l *zap.SugaredLogger, o *origin, replica types.AdGuardInstance) {
rl.Info("Sync done") rl.Info("Sync done")
} }
func syncServices(os *types.Services, replica client.Client) error { func (w *worker) syncServices(os *types.Services, replica client.Client) error {
rs, err := replica.Services() rs, err := replica.Services()
if err != nil { if err != nil {
return err return err
@@ -136,7 +210,7 @@ func syncServices(os *types.Services, replica client.Client) error {
return nil return nil
} }
func syncFilters(of *types.FilteringStatus, replica client.Client) error { func (w *worker) syncFilters(of *types.FilteringStatus, replica client.Client) error {
rf, err := replica.Filtering() rf, err := replica.Filtering()
if err != nil { if err != nil {
return err return err
@@ -185,7 +259,7 @@ func syncFilters(of *types.FilteringStatus, replica client.Client) error {
return nil return nil
} }
func syncRewrites(or *types.RewriteEntries, replica client.Client) error { func (w *worker) syncRewrites(or *types.RewriteEntries, replica client.Client) error {
replicaRewrites, err := replica.RewriteList() replicaRewrites, err := replica.RewriteList()
if err != nil { if err != nil {
@@ -203,7 +277,7 @@ func syncRewrites(or *types.RewriteEntries, replica client.Client) error {
return nil return nil
} }
func syncClients(oc *types.Clients, replica client.Client) error { func (w *worker) syncClients(oc *types.Clients, replica client.Client) error {
rc, err := replica.Clients() rc, err := replica.Clients()
if err != nil { if err != nil {
return err return err
@@ -223,10 +297,69 @@ func syncClients(oc *types.Clients, replica client.Client) error {
return nil return nil
} }
type origin struct { func (w *worker) syncGeneralSettings(o *origin, rs *types.Status, replica client.Client) error {
status *types.Status if o.status.ProtectionEnabled != rs.ProtectionEnabled {
rewrites *types.RewriteEntries if err := replica.ToggleProtection(o.status.ProtectionEnabled); err != nil {
services *types.Services return err
filters *types.FilteringStatus }
clients *types.Clients }
if rp, err := replica.Parental(); err != nil {
return err
} else if o.parental != rp {
if err = replica.ToggleParental(o.parental); err != nil {
return err
}
}
if rs, err := replica.SafeSearch(); err != nil {
return err
} else if o.safeSearch != rs {
if err = replica.ToggleSafeSearch(o.safeSearch); err != nil {
return err
}
}
if rs, err := replica.SafeBrowsing(); err != nil {
return err
} else if o.safeBrowsing != rs {
if err = replica.ToggleSafeBrowsing(o.safeBrowsing); err != nil {
return err
}
}
return nil
}
func (w *worker) syncConfigs(o *origin, rs *types.Status, replica client.Client) error {
qlc, err := replica.QueryLogConfig()
if err != nil {
return err
}
if !o.queryLogConfig.Equals(qlc) {
if err = replica.SetQueryLogConfig(o.queryLogConfig.Enabled, o.queryLogConfig.Interval, o.queryLogConfig.AnonymizeClientIP); err != nil {
return err
}
}
sc, err := replica.StatsConfig()
if err != nil {
return err
}
if o.statsConfig.Interval != sc.Interval {
if err = replica.SetStatsConfig(o.statsConfig.Interval); err != nil {
return err
}
}
return nil
}
type origin struct {
status *types.Status
rewrites *types.RewriteEntries
services *types.Services
filters *types.FilteringStatus
clients *types.Clients
queryLogConfig *types.QueryLogConfig
statsConfig *types.IntervalConfig
parental bool
safeSearch bool
safeBrowsing bool
} }

View File

@@ -5,32 +5,47 @@ import (
"fmt" "fmt"
"sort" "sort"
"strings" "strings"
"time"
) )
// Config application configuration struct
type Config struct { type Config struct {
Origin AdGuardInstance `json:"origin" yaml:"origin"` Origin AdGuardInstance `json:"origin" yaml:"origin"`
Replica *AdGuardInstance `json:"replica,omitempty" yaml:"replica,omitempty"` Replica AdGuardInstance `json:"replica,omitempty" yaml:"replica,omitempty"`
Replicas []AdGuardInstance `json:"replicas,omitempty" yaml:"replicas,omitempty"` Replicas []AdGuardInstance `json:"replicas,omitempty" yaml:"replicas,omitempty"`
Cron string `json:"cron,omitempty" yaml:"cron,omitempty"` Cron string `json:"cron,omitempty" yaml:"cron,omitempty"`
API API `json:"api,omitempty" yaml:"api,omitempty"`
} }
// API configuration
type API struct {
Port int `json:"port,omitempty" yaml:"port,omitempty"`
Username string `json:"username,omitempty" yaml:"username,omitempty"`
Password string `json:"password,omitempty" yaml:"password,omitempty"`
}
// UniqueReplicas get unique replication instances
func (cfg *Config) UniqueReplicas() []AdGuardInstance { func (cfg *Config) UniqueReplicas() []AdGuardInstance {
dedup := make(map[string]AdGuardInstance) dedup := make(map[string]AdGuardInstance)
if cfg.Replica != nil { if cfg.Replica.URL != "" {
dedup[cfg.Replica.Key()] = *cfg.Replica dedup[cfg.Replica.Key()] = cfg.Replica
} }
for _, replica := range cfg.Replicas { for _, replica := range cfg.Replicas {
dedup[replica.Key()] = replica if replica.URL != "" {
dedup[replica.Key()] = replica
}
} }
var r []AdGuardInstance var r []AdGuardInstance
for _, replica := range dedup { for _, replica := range dedup {
if replica.APIPath == "" {
replica.APIPath = "/control"
}
r = append(r, replica) r = append(r, replica)
} }
return r return r
} }
// AdGuardInstance adguard home config instance
type AdGuardInstance struct { type AdGuardInstance struct {
URL string `json:"url" yaml:"url"` URL string `json:"url" yaml:"url"`
APIPath string `json:"apiPath,omitempty" yaml:"apiPath,omitempty"` APIPath string `json:"apiPath,omitempty" yaml:"apiPath,omitempty"`
@@ -39,23 +54,32 @@ type AdGuardInstance struct {
InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify"` InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify"`
} }
// Key AdGuardInstance key
func (i *AdGuardInstance) Key() string { func (i *AdGuardInstance) Key() string {
return fmt.Sprintf("%s%s", i.URL, i.APIPath) return fmt.Sprintf("%s%s", i.URL, i.APIPath)
} }
type Status struct { // Protection API struct
DNSAddresses []string `json:"dns_addresses"` type Protection struct {
DNSPort int `json:"dns_port"` ProtectionEnabled bool `json:"protection_enabled"`
HTTPPort int `json:"http_port"`
ProtectionEnabled bool `json:"protection_enabled"`
DhcpAvailable bool `json:"dhcp_available"`
Running bool `json:"running"`
Version string `json:"version"`
Language string `json:"language"`
} }
// Status API struct
type Status struct {
Protection
DNSAddresses []string `json:"dns_addresses"`
DNSPort int `json:"dns_port"`
HTTPPort int `json:"http_port"`
DhcpAvailable bool `json:"dhcp_available"`
Running bool `json:"running"`
Version string `json:"version"`
Language string `json:"language"`
}
// RewriteEntries list of RewriteEntry
type RewriteEntries []RewriteEntry type RewriteEntries []RewriteEntry
// Merge RewriteEntries
func (rwe *RewriteEntries) Merge(other *RewriteEntries) (RewriteEntries, RewriteEntries) { func (rwe *RewriteEntries) Merge(other *RewriteEntries) (RewriteEntries, RewriteEntries) {
current := make(map[string]RewriteEntry) current := make(map[string]RewriteEntry)
@@ -80,27 +104,31 @@ func (rwe *RewriteEntries) Merge(other *RewriteEntries) (RewriteEntries, Rewrite
return adds, removes return adds, removes
} }
// RewriteEntry API struct
type RewriteEntry struct { type RewriteEntry struct {
Domain string `json:"domain"` Domain string `json:"domain"`
Answer string `json:"answer"` Answer string `json:"answer"`
} }
// Key RewriteEntry key
func (re *RewriteEntry) Key() string { func (re *RewriteEntry) Key() string {
return fmt.Sprintf("%s#%s", re.Domain, re.Answer) return fmt.Sprintf("%s#%s", re.Domain, re.Answer)
} }
// Filters list of Filter
type Filters []Filter type Filters []Filter
// Filter API struct
type Filter struct { type Filter struct {
ID int `json:"id"` ID int `json:"id"`
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
URL string `json:"url"` // needed for add URL string `json:"url"` // needed for add
Name string `json:"name"` // needed for add Name string `json:"name"` // needed for add
RulesCount int `json:"rules_count"` RulesCount int `json:"rules_count"`
LastUpdated time.Time `json:"last_updated"` Whitelist bool `json:"whitelist"` // needed for add
Whitelist bool `json:"whitelist"` // needed for add
} }
// FilteringStatus API struct
type FilteringStatus struct { type FilteringStatus struct {
FilteringConfig FilteringConfig
Filters Filters `json:"filters"` Filters Filters `json:"filters"`
@@ -108,21 +136,48 @@ type FilteringStatus struct {
UserRules UserRules `json:"user_rules"` UserRules UserRules `json:"user_rules"`
} }
// UserRules API struct
type UserRules []string type UserRules []string
// String toString of Users
func (ur UserRules) String() string { func (ur UserRules) String() string {
return strings.Join(ur, "\n") return strings.Join(ur, "\n")
} }
type FilteringConfig struct { // EnableConfig API struct
Enabled bool `json:"enabled"` type EnableConfig struct {
Interval int `json:"interval"` Enabled bool `json:"enabled"`
} }
// IntervalConfig API struct
type IntervalConfig struct {
Interval int `json:"interval"`
}
// FilteringConfig API struct
type FilteringConfig struct {
EnableConfig
IntervalConfig
}
// QueryLogConfig API struct
type QueryLogConfig struct {
EnableConfig
IntervalConfig
AnonymizeClientIP bool `json:"anonymize_client_ip"`
}
// Equals QueryLogConfig equal check
func (qlc *QueryLogConfig) Equals(o *QueryLogConfig) bool {
return qlc.Enabled == o.Enabled && qlc.AnonymizeClientIP == o.AnonymizeClientIP && qlc.Interval == o.Interval
}
// RefreshFilter API struct
type RefreshFilter struct { type RefreshFilter struct {
Whitelist bool `json:"whitelist"` Whitelist bool `json:"whitelist"`
} }
// Merge merge RefreshFilters
func (fs *Filters) Merge(other Filters) (Filters, Filters) { func (fs *Filters) Merge(other Filters) (Filters, Filters) {
current := make(map[string]Filter) current := make(map[string]Filter)
@@ -147,18 +202,22 @@ func (fs *Filters) Merge(other Filters) (Filters, Filters) {
return adds, removes return adds, removes
} }
// Services API struct
type Services []string type Services []string
// Sort sort Services
func (s Services) Sort() { func (s Services) Sort() {
sort.Strings(s) sort.Strings(s)
} }
// Equals Services equal check
func (s *Services) Equals(o *Services) bool { func (s *Services) Equals(o *Services) bool {
s.Sort() s.Sort()
o.Sort() o.Sort()
return equals(*s, *o) return equals(*s, *o)
} }
// Clients API struct
type Clients struct { type Clients struct {
Clients []Client `json:"clients"` Clients []Client `json:"clients"`
AutoClients []struct { AutoClients []struct {
@@ -171,6 +230,7 @@ type Clients struct {
SupportedTags []string `json:"supported_tags"` SupportedTags []string `json:"supported_tags"`
} }
// Client API struct
type Client struct { type Client struct {
Ids []string `json:"ids"` Ids []string `json:"ids"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
@@ -188,6 +248,7 @@ type Client struct {
DisallowedRule string `json:"disallowed_rule"` DisallowedRule string `json:"disallowed_rule"`
} }
// Sort sort clients
func (cl *Client) Sort() { func (cl *Client) Sort() {
sort.Strings(cl.Ids) sort.Strings(cl.Ids)
sort.Strings(cl.Tags) sort.Strings(cl.Tags)
@@ -195,6 +256,7 @@ func (cl *Client) Sort() {
sort.Strings(cl.Upstreams) sort.Strings(cl.Upstreams)
} }
// Equal Clients equal check
func (cl *Client) Equal(o *Client) bool { func (cl *Client) Equal(o *Client) bool {
cl.Sort() cl.Sort()
o.Sort() o.Sort()
@@ -204,6 +266,7 @@ func (cl *Client) Equal(o *Client) bool {
return string(a) == string(b) return string(a) == string(b)
} }
// Merge merge Clients
func (clients *Clients) Merge(other *Clients) ([]Client, []Client, []Client) { func (clients *Clients) Merge(other *Clients) ([]Client, []Client, []Client) {
current := make(map[string]Client) current := make(map[string]Client)
for _, client := range clients.Clients { for _, client := range clients.Clients {
@@ -237,6 +300,7 @@ func (clients *Clients) Merge(other *Clients) ([]Client, []Client, []Client) {
return adds, updates, removes return adds, updates, removes
} }
// ClientUpdate API struct
type ClientUpdate struct { type ClientUpdate struct {
Name string `json:"name"` Name string `json:"name"`
Data Client `json:"data"` Data Client `json:"data"`

View File

@@ -0,0 +1,13 @@
package types_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestTypes(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Types Suite")
}

23
pkg/types/types_test.go Normal file
View File

@@ -0,0 +1,23 @@
package types_test
import (
"encoding/json"
"io/ioutil"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/bakito/adguardhome-sync/pkg/types"
)
var _ = Describe("Types", func() {
Context("FilteringStatus", func() {
It("should correctly parse json", func() {
b, err := ioutil.ReadFile("../..//testdata/filtering-status.json")
fs := &types.FilteringStatus{}
Ω(err).ShouldNot(HaveOccurred())
err = json.Unmarshal(b, fs)
Ω(err).ShouldNot(HaveOccurred())
})
})
})

View File

@@ -0,0 +1,28 @@
[Unit]
Description=AdGuard Home Sync service
ConditionFileIsExecutable=/opt/AdGuardHomeSync/adguardhome-sync
Requires=network.target
After=network-online.target syslog.target
[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/opt/AdGuardHomeSync/adguardhome-sync "run" "--config" "/opt/AdGuardHomeSync/adguardhome-sync.yaml"
WorkingDirectory=/opt/AdGuardHome
Restart=on-success
SuccessExitStatus=1 2 8 SIGKILL
RestartSec=120
EnvironmentFile=-/etc/sysconfig/GoServiceExampleLogging
StandardOutput=file:/var/log/AdGuardHomeSync.out
StandardError=file:/var/log/AdGuardHomeSync.err
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target

11
systemd/README.md Normal file
View File

@@ -0,0 +1,11 @@
# Install
```bash
mkdir -p /opt/AdGuardHomeSync/
sudo cp adguardhome-sync /opt/AdGuardHomeSync/adguardhome-sync
sudo cp adguardhome-sync.yaml /opt/AdGuardHomeSync/adguardhome-sync.yaml
sudo cp AdGuardHomeSync.service /etc/systemd/system/AdGuardHomeSync.service
sudo systemctl enable AdGuardHomeSync
```

27
testdata/filtering-status.json vendored Normal file
View File

@@ -0,0 +1,27 @@
{
"enabled": true,
"interval": 24,
"filters": [
{
"id": 1616956420,
"enabled": true,
"url": "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt",
"name": "AdGuard DNS filter",
"rules_count": 37330,
"last_updated": ""
},
{
"id": 1616956421,
"enabled": true,
"url": "https://adaway.org/hosts.txt",
"name": "AdAway Default Blocklist",
"rules_count": 8717,
"last_updated": "2021-04-04T20:35:03+01:00"
}
],
"whitelist_filters": null,
"user_rules": [
"||metrics2.data.hicloud.com^$important",
""
]
}

View File

@@ -1,49 +0,0 @@
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
"strings"
"github.com/coreos/go-semver/semver"
)
func main() {
out, err := exec.Command("git", "branch", "--show-current").Output()
if err != nil {
panic(err)
}
branch := strings.TrimSpace(string(out))
if branch != "main" {
panic(fmt.Errorf(`error: must be in "master" branch, current branch: %q`, branch))
}
out, err = exec.Command("git", "describe").Output()
if err != nil {
panic(err)
}
version := strings.TrimPrefix(strings.TrimSpace(string(out)), "v")
v := semver.New(version)
v.BumpPatch()
reader := bufio.NewReader(os.Stdin)
if _, err = fmt.Fprintf(os.Stderr, "Enter Release Version: [v%v] ", v); err != nil {
panic(err)
}
text, err := reader.ReadString('\n')
if err != nil {
panic(err)
}
if strings.HasPrefix(text, "v") {
text = text[1:]
v = semver.New(strings.TrimSpace(text))
}
if _, err = fmt.Fprintf(os.Stderr, "Using Version: v%v\n", v); err != nil {
panic(err)
}
fmt.Printf("v%v", v)
}