Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64463b6842 | ||
|
|
9450c09e2a | ||
|
|
514b228739 | ||
|
|
06f95de085 | ||
|
|
1986e87518 | ||
|
|
cf0a381f80 | ||
|
|
5e591e04c3 | ||
|
|
fcf25538c0 | ||
|
|
aa95031136 | ||
|
|
6fb2dd12a8 | ||
|
|
8db9c98644 | ||
|
|
f8578e85b2 | ||
|
|
5dafdf5472 | ||
|
|
23d5f30eb8 | ||
|
|
44609a93e3 | ||
|
|
70e60bb7d0 | ||
|
|
aacffb1518 |
44
.github/workflows/publish.yml
vendored
Normal file
44
.github/workflows/publish.yml
vendored
Normal 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
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
|||||||
.idea
|
.idea
|
||||||
coverage.out
|
coverage.out
|
||||||
dist
|
dist
|
||||||
|
adguardhome-sync
|
||||||
|
main
|
||||||
|
.adguardhome-sync.yaml
|
||||||
|
|||||||
@@ -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
27
Dockerfile
Normal 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
|
||||||
11
Makefile
11
Makefile
@@ -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
104
README.md
@@ -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
|
||||||
|
|
||||||
|
```
|
||||||
|
|||||||
58
cmd/root.go
58
cmd/root.go
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
23
cmd/run.go
23
cmd/run.go
@@ -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
5
go.mod
@@ -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
72
go.sum
@@ -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=
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
126
pkg/sync/http.go
Normal 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)
|
||||||
|
}
|
||||||
193
pkg/sync/sync.go
193
pkg/sync/sync.go
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
|||||||
13
pkg/types/types_suite_test.go
Normal file
13
pkg/types/types_suite_test.go
Normal 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
23
pkg/types/types_test.go
Normal 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())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
28
systemd/AdGuardHomeSync.service
Normal file
28
systemd/AdGuardHomeSync.service
Normal 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
11
systemd/README.md
Normal 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
27
testdata/filtering-status.json
vendored
Normal 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",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user