feat: implement stricter lint rules git golanci-lint v2 (#538)

* feat: implement stricter lint rules git golanci-lint v2

* fix lint issues

* fix lint issues
This commit is contained in:
Marc Brugger
2025-03-25 21:30:22 +01:00
committed by GitHub
parent 2bff464f65
commit 02ff6a11f0
46 changed files with 625 additions and 443 deletions

View File

@@ -22,10 +22,8 @@ jobs:
with: with:
go-version-file: "go.mod" go-version-file: "go.mod"
- name: golangci-lint - name: Lint
uses: golangci/golangci-lint-action@v6 run: make lint
with:
skip-cache: true
test: test:
name: test name: test

209
.golangci.yaml Normal file
View File

@@ -0,0 +1,209 @@
version: '2'
linters:
enable:
- asciicheck
- bidichk
- bodyclose
- canonicalheader
- containedctx
- copyloopvar
- decorder
- dogsled
- dupword
- durationcheck
- err113
- errname
- errorlint
- exptostd
- fatcontext
- forcetypeassert
- gocheckcompilerdirectives
- gochecksumtype
- gocritic
- godot
- gomodguard
- goprintffuncname
- gosmopolitan
- grouper
- iface
- importas
- inamedparam
- interfacebloat
- intrange
- loggercheck
- makezero
- mirror
- misspell
- nilerr
- nilnesserr
- noctx
- nolintlint
- nosprintfhostport
- perfsprint
- predeclared
- promlinter
- protogetter
- reassign
- revive
- rowserrcheck
- sloglint
- spancheck
- sqlclosecheck
- staticcheck
- tagalign
- testableexamples
- testifylint
- thelper
- unconvert
- unparam
- usestdlibvars
- usetesting
- wastedassign
- whitespace
- zerologlint
disable:
- asasalint
- contextcheck
- cyclop
- depguard
- dupl
- errchkjson
- exhaustive
- exhaustruct
- forbidigo
- funlen
- ginkgolinter
- gochecknoglobals
- gochecknoinits
- gocognit
- goconst
- gocyclo
- godox
- goheader
- gomoddirectives
- gosec
- ireturn
- lll
- maintidx
- musttag
- nakedret
- nestif
- nilnil
- nlreturn
- nonamedreturns
- paralleltest
- prealloc
- recvcheck
- tagliatelle
- testpackage
- tparallel
- varnamelen
- wrapcheck
- wsl
settings:
gocritic:
enable-all: true
disabled-checks:
- emptyFallthrough
- hugeParam
- rangeValCopy
- unnamedResult
- whyNoLint
govet:
disable:
- fieldalignment
- shadow
enable-all: true
misspell:
locale: US
revive:
enable-all-rules: true
rules:
- name: add-constant
disabled: true
- name: cognitive-complexity
disabled: true
- name: cyclomatic
disabled: true
- name: deep-exit
disabled: true
- name: dot-imports
severity: warning
disabled: false
exclude: [""]
arguments:
- allowedPackages: ["github.com/onsi/ginkgo/v2", "github.com/onsi/gomega"]
- name: empty-block
disabled: true
- name: exported
disabled: true
- name: filename-format
arguments:
- ^[a-z][-0-9_a-z]*(?:\.gen)?\.go$
- name: flag-parameter
disabled: true
- name: function-length
disabled: true
- name: function-result-limit
disabled: true
- name: import-shadowing
disabled: true
- name: line-length-limit
disabled: true
- name: max-control-nesting
disabled: true
- name: max-public-structs
disabled: true
- name: nested-structs
disabled: true
- name: package-comments
disabled: true
- name: unused-parameter
disabled: true
- name: unused-receiver
disabled: true
staticcheck:
checks:
- 'all'
- '-ST1000'
exclusions:
generated: lax
presets:
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- err113
text: do not define dynamic errors, use wrapped static errors instead
- linters:
- forbidigo
path: ^internal/cmds/
- linters:
- forcetypeassert
path: _test\.go$
- linters:
- forbidigo
path: assets/scripts/generate-commit.go
formatters:
enable:
- gci
- gofmt
- gofumpt
- goimports
- golines
settings:
gci:
sections:
- standard
- default
- prefix(github.com/bakito/adguardhome-sync)
gofumpt:
module-path: github.com/bakito/adguardhome-sync
extra-rules: true
goimports:
local-prefixes:
- github.com/bakito/adguardhome-sync
golines:
max-len: 128
tab-len: 4

View File

@@ -1,37 +0,0 @@
run:
timeout: 5m
linters:
enable:
- asciicheck
- bodyclose
- dogsled
- durationcheck
- errcheck
- errorlint
- gci
- gofmt
- gofumpt
- goimports
- gosec
- gosimple
- govet
- importas
- ineffassign
- misspell
- nakedret
- nolintlint
- staticcheck
- unconvert
- unparam
- unused
linters-settings:
gosec:
# Exclude generated files
exclude-generated: true
excludes:
- G601 # not applicable in go 1.22 anymore
gofmt:
# simplify code: gofmt with `-s` option, true by default
simplify: true

View File

@@ -24,8 +24,8 @@ TB_SEMVER ?= $(TB_LOCALBIN)/semver
TB_DEEPCOPY_GEN_VERSION ?= v0.32.3 TB_DEEPCOPY_GEN_VERSION ?= v0.32.3
# renovate: packageName=mvdan.cc/gofumpt # renovate: packageName=mvdan.cc/gofumpt
TB_GOFUMPT_VERSION ?= v0.7.0 TB_GOFUMPT_VERSION ?= v0.7.0
# renovate: packageName=github.com/golangci/golangci-lint/cmd/golangci-lint # renovate: packageName=github.com/golangci/golangci-lint/v2/cmd/golangci-lint
TB_GOLANGCI_LINT_VERSION ?= v1.64.8 TB_GOLANGCI_LINT_VERSION ?= v2.0.1
# renovate: packageName=github.com/segmentio/golines # renovate: packageName=github.com/segmentio/golines
TB_GOLINES_VERSION ?= v0.12.2 TB_GOLINES_VERSION ?= v0.12.2
# renovate: packageName=github.com/goreleaser/goreleaser/v2 # renovate: packageName=github.com/goreleaser/goreleaser/v2
@@ -53,7 +53,7 @@ $(TB_GOFUMPT): $(TB_LOCALBIN)
.PHONY: tb.golangci-lint .PHONY: tb.golangci-lint
tb.golangci-lint: $(TB_GOLANGCI_LINT) ## Download golangci-lint locally if necessary. tb.golangci-lint: $(TB_GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
$(TB_GOLANGCI_LINT): $(TB_LOCALBIN) $(TB_GOLANGCI_LINT): $(TB_LOCALBIN)
test -s $(TB_LOCALBIN)/golangci-lint || GOBIN=$(TB_LOCALBIN) go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(TB_GOLANGCI_LINT_VERSION) test -s $(TB_LOCALBIN)/golangci-lint || GOBIN=$(TB_LOCALBIN) go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(TB_GOLANGCI_LINT_VERSION)
.PHONY: tb.golines .PHONY: tb.golines
tb.golines: $(TB_GOLINES) ## Download golines locally if necessary. tb.golines: $(TB_GOLINES) ## Download golines locally if necessary.
$(TB_GOLINES): $(TB_LOCALBIN) $(TB_GOLINES): $(TB_LOCALBIN)
@@ -95,7 +95,7 @@ tb.update: tb.reset
toolbox makefile --renovate -f $(TB_LOCALDIR)/Makefile \ toolbox makefile --renovate -f $(TB_LOCALDIR)/Makefile \
k8s.io/code-generator/cmd/deepcopy-gen@github.com/kubernetes/code-generator \ k8s.io/code-generator/cmd/deepcopy-gen@github.com/kubernetes/code-generator \
mvdan.cc/gofumpt@github.com/mvdan/gofumpt \ mvdan.cc/gofumpt@github.com/mvdan/gofumpt \
github.com/golangci/golangci-lint/cmd/golangci-lint \ github.com/golangci/golangci-lint/v2/cmd/golangci-lint \
github.com/segmentio/golines \ github.com/segmentio/golines \
github.com/goreleaser/goreleaser/v2 \ github.com/goreleaser/goreleaser/v2 \
go.uber.org/mock/mockgen@github.com/uber-go/mock \ go.uber.org/mock/mockgen@github.com/uber-go/mock \

View File

@@ -4,9 +4,10 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/spf13/cobra"
"github.com/bakito/adguardhome-sync/pkg/log" "github.com/bakito/adguardhome-sync/pkg/log"
"github.com/bakito/adguardhome-sync/version" "github.com/bakito/adguardhome-sync/version"
"github.com/spf13/cobra"
) )
var ( var (
@@ -14,7 +15,7 @@ var (
logger = log.GetLogger("root") logger = log.GetLogger("root")
) )
// rootCmd represents the base command when called without any subcommands // rootCmd represents the base command when called without any subcommands.
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "adguardhome-sync", Use: "adguardhome-sync",
Short: "Synchronize config from one AdGuardHome instance to another", Short: "Synchronize config from one AdGuardHome instance to another",
@@ -25,7 +26,7 @@ var rootCmd = &cobra.Command{
// This is called by main.main(). It only needs to happen once to the rootCmd. // This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() { func Execute() {
if err := rootCmd.Execute(); err != nil { if err := rootCmd.Execute(); err != nil {
fmt.Println(err) _, _ = fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
} }

View File

@@ -1,16 +1,17 @@
package cmd package cmd
import ( import (
"github.com/spf13/cobra"
"github.com/bakito/adguardhome-sync/pkg/config" "github.com/bakito/adguardhome-sync/pkg/config"
"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/spf13/cobra"
) )
// runCmd represents the run command // runCmd represents the run command.
var doCmd = &cobra.Command{ var doCmd = &cobra.Command{
Use: "run", Use: "run",
Short: "Start a synchronisation from origin to replica", Short: "Start a synchronization 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`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
logger = log.GetLogger("run") logger = log.GetLogger("run")
@@ -44,21 +45,21 @@ func init() {
doCmd.PersistentFlags().Bool(config.FlagRunOnStart, true, "Run the sync job on start.") doCmd.PersistentFlags().Bool(config.FlagRunOnStart, true, "Run the sync job on start.")
doCmd.PersistentFlags().Bool(config.FlagPrintConfigOnly, false, "Prints the configuration only and exists. "+ doCmd.PersistentFlags().Bool(config.FlagPrintConfigOnly, false, "Prints the configuration only and exists. "+
"Can be used to debug the config E.g: when having authentication issues.") "Can be used to debug the config E.g: when having authentication issues.")
doCmd.PersistentFlags().Bool(config.FlagContinueOnError, false, "If enabled, the synchronisation task "+ doCmd.PersistentFlags().Bool(config.FlagContinueOnError, false, "If enabled, the synchronization task "+
"will not fail on single errors, but will log the errors and continue.") "will not fail on single errors, but will log the errors and continue.")
doCmd.PersistentFlags(). doCmd.PersistentFlags().
Int(config.FlagApiPort, 8080, "Sync API Port, the API endpoint will be started to enable remote triggering; if 0 port API is disabled.") Int(config.FlagAPIPort, 8080, "Sync API Port, the API endpoint will be started to enable remote triggering; if 0 port API is disabled.")
doCmd.PersistentFlags().String(config.FlagApiUsername, "", "Sync API username") doCmd.PersistentFlags().String(config.FlagAPIUsername, "", "Sync API username")
doCmd.PersistentFlags().String(config.FlagApiPassword, "", "Sync API password") doCmd.PersistentFlags().String(config.FlagAPIPassword, "", "Sync API password")
doCmd.PersistentFlags().String(config.FlagApiDarkMode, "", "API UI in dark mode") doCmd.PersistentFlags().String(config.FlagAPIDarkMode, "", "API UI in dark mode")
doCmd.PersistentFlags().Bool(config.FlagFeatureDhcpServerConfig, true, "Enable DHCP server config feature") doCmd.PersistentFlags().Bool(config.FlagFeatureDhcpServerConfig, true, "Enable DHCP server config feature")
doCmd.PersistentFlags().Bool(config.FlagFeatureDhcpStaticLeases, true, "Enable DHCP server static leases feature") doCmd.PersistentFlags().Bool(config.FlagFeatureDhcpStaticLeases, true, "Enable DHCP server static leases feature")
doCmd.PersistentFlags().Bool(config.FlagFeatureDnsServerConfig, true, "Enable DNS server config feature") doCmd.PersistentFlags().Bool(config.FlagFeatureDNSServerConfig, true, "Enable DNS server config feature")
doCmd.PersistentFlags().Bool(config.FlagFeatureDnsAccessLists, true, "Enable DNS server access lists feature") doCmd.PersistentFlags().Bool(config.FlagFeatureDNSAccessLists, true, "Enable DNS server access lists feature")
doCmd.PersistentFlags().Bool(config.FlagFeatureDnsRewrites, true, "Enable DNS rewrites feature") doCmd.PersistentFlags().Bool(config.FlagFeatureDNSRewrites, true, "Enable DNS rewrites feature")
doCmd.PersistentFlags().Bool(config.FlagFeatureGeneral, true, "Enable general settings feature") doCmd.PersistentFlags().Bool(config.FlagFeatureGeneral, true, "Enable general settings feature")
doCmd.PersistentFlags().Bool(config.FlagFeatureQueryLog, true, "Enable query log config feature") doCmd.PersistentFlags().Bool(config.FlagFeatureQueryLog, true, "Enable query log config feature")
@@ -70,7 +71,7 @@ func init() {
doCmd.PersistentFlags().String(config.FlagOriginURL, "", "Origin instance url") doCmd.PersistentFlags().String(config.FlagOriginURL, "", "Origin instance url")
doCmd.PersistentFlags(). doCmd.PersistentFlags().
String(config.FlagOriginWebURL, "", "Origin instance web url used in the web interface (default: <origin-url>)") String(config.FlagOriginWebURL, "", "Origin instance web url used in the web interface (default: <origin-url>)")
doCmd.PersistentFlags().String(config.FlagOriginApiPath, "/control", "Origin instance API path") doCmd.PersistentFlags().String(config.FlagOriginAPIPath, "/control", "Origin instance API path")
doCmd.PersistentFlags().String(config.FlagOriginUsername, "", "Origin instance username") doCmd.PersistentFlags().String(config.FlagOriginUsername, "", "Origin instance username")
doCmd.PersistentFlags().String(config.FlagOriginPassword, "", "Origin instance password") doCmd.PersistentFlags().String(config.FlagOriginPassword, "", "Origin instance password")
doCmd.PersistentFlags().String(config.FlagOriginCookie, "", "If Set, uses a cookie for authentication") doCmd.PersistentFlags().String(config.FlagOriginCookie, "", "If Set, uses a cookie for authentication")
@@ -79,7 +80,7 @@ func init() {
doCmd.PersistentFlags().String(config.FlagReplicaURL, "", "Replica instance url") doCmd.PersistentFlags().String(config.FlagReplicaURL, "", "Replica instance url")
doCmd.PersistentFlags(). doCmd.PersistentFlags().
String(config.FlagReplicaWebURL, "", "Replica instance web url used in the web interface (default: <replica-url>)") String(config.FlagReplicaWebURL, "", "Replica instance web url used in the web interface (default: <replica-url>)")
doCmd.PersistentFlags().String(config.FlagReplicaApiPath, "/control", "Replica instance API path") doCmd.PersistentFlags().String(config.FlagReplicaAPIPath, "/control", "Replica instance API path")
doCmd.PersistentFlags().String(config.FlagReplicaUsername, "", "Replica instance username") doCmd.PersistentFlags().String(config.FlagReplicaUsername, "", "Replica instance username")
doCmd.PersistentFlags().String(config.FlagReplicaPassword, "", "Replica instance password") doCmd.PersistentFlags().String(config.FlagReplicaPassword, "", "Replica instance password")
doCmd.PersistentFlags().String(config.FlagReplicaCookie, "", "If Set, uses a cookie for authentication") doCmd.PersistentFlags().String(config.FlagReplicaCookie, "", "If Set, uses a cookie for authentication")

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"log" "log"
@@ -20,21 +21,33 @@ func main() {
} }
log.Printf("Patching schema version %s\n", version) log.Printf("Patching schema version %s\n", version)
resp, err := http.Get( ctx := context.Background() // Or use context.WithTimeout
req, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
fmt.Sprintf("https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/%s/openapi/openapi.yaml", version), fmt.Sprintf("https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/%s/openapi/openapi.yaml", version),
http.NoBody,
) )
if err != nil {
log.Fatal(err)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
defer func() { _ = resp.Body.Close() }() defer func() { _ = resp.Body.Close() }()
data, err := io.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
log.Fatalln(err) log.Println(err)
return
} }
schema := make(map[string]interface{}) schema := make(map[string]any)
err = yaml.Unmarshal(data, &schema) err = yaml.Unmarshal(data, &schema)
if err != nil { if err != nil {
log.Fatalln(err) log.Println(err)
return
} }
if requestBodies, ok, _ := unstructured.NestedMap(schema, "components", "requestBodies"); ok { if requestBodies, ok, _ := unstructured.NestedMap(schema, "components", "requestBodies"); ok {
@@ -47,9 +60,11 @@ func main() {
"paths", "/dns_info", "get", "responses", "200", "content", "application/json", "schema"); ok { "paths", "/dns_info", "get", "responses", "200", "content", "application/json", "schema"); ok {
if allOf, ok, _ := unstructured.NestedSlice(dnsInfo, "allOf"); ok && len(allOf) == 2 { if allOf, ok, _ := unstructured.NestedSlice(dnsInfo, "allOf"); ok && len(allOf) == 2 {
delete(dnsInfo, "allOf") delete(dnsInfo, "allOf")
if err := unstructured.SetNestedMap(schema, allOf[0].(map[string]interface{}), //nolint:forcetypeassert
if err := unstructured.SetNestedMap(schema, allOf[0].(map[string]any),
"paths", "/dns_info", "get", "responses", "200", "content", "application/json", "schema"); err != nil { "paths", "/dns_info", "get", "responses", "200", "content", "application/json", "schema"); err != nil {
log.Fatalln(err) log.Println(err)
return
} }
} }
} }
@@ -60,24 +75,26 @@ func main() {
b, err := yaml.Marshal(&schema) b, err := yaml.Marshal(&schema)
if err != nil { if err != nil {
log.Fatalln(err) log.Println(err)
return
} }
log.Printf("Writing schema file tmp/%s", fileName) log.Printf("Writing schema file tmp/%s", fileName)
err = os.WriteFile("tmp/"+fileName, b, 0o600) err = os.WriteFile("tmp/"+fileName, b, 0o600)
if err != nil { if err != nil {
log.Fatalln(err) log.Println(err)
return
} }
} }
func correctEntries(schema map[string]interface{}) { func correctEntries(schema map[string]any) {
// https://github.com/AdguardTeam/AdGuardHome/pull/7678 // https://github.com/AdguardTeam/AdGuardHome/pull/7678
if err := unstructured.SetNestedField(schema, "string", "components", "schemas", "QueryLogItem", "properties", "client_proto", "type"); err != nil { if err := unstructured.SetNestedField(schema, "string", "components", "schemas", "QueryLogItem", "properties", "client_proto", "type"); err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
} }
func addFakeTags(schema map[string]interface{}) { func addFakeTags(schema map[string]any) {
fake := map[string]interface{}{"faker": `slice_len=24`} fake := map[string]any{"faker": `slice_len=24`}
if err := unstructured.SetNestedMap(schema, fake, "components", "schemas", "Stats", "properties", "blocked_filtering", "x-oapi-codegen-extra-tags"); err != nil { if err := unstructured.SetNestedMap(schema, fake, "components", "schemas", "Stats", "properties", "blocked_filtering", "x-oapi-codegen-extra-tags"); err != nil {
log.Fatalln(err) log.Fatalln(err)
} }

View File

@@ -17,14 +17,18 @@ func (cl *client) doGet(req *resty.Request, url string) error {
rl.Debug("do get") rl.Debug("do get")
resp, err := req.Get(url) resp, err := req.Get(url)
if err != nil { if err != nil {
if resp != nil && resp.StatusCode() == http.StatusFound { l := rl
loc := resp.Header().Get("Location") if resp != nil {
if loc == "/install.html" || loc == "/control/install.html" { if resp.StatusCode() == http.StatusFound {
return ErrSetupNeeded loc := resp.Header().Get("Location")
if loc == "/install.html" || loc == "/control/install.html" {
return ErrSetupNeeded
}
} }
l = l.With("status", resp.StatusCode(), "body", string(resp.Body()), "error", err)
} }
rl.With("status", resp.StatusCode(), "body", string(resp.Body()), "error", err).Debug("error in do get") l.Debug("error in do get")
return detailedError(resp, err) return detailedError(resp, err)
} }

View File

@@ -11,19 +11,20 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/go-resty/resty/v2"
"go.uber.org/zap"
"github.com/bakito/adguardhome-sync/pkg/client/model" "github.com/bakito/adguardhome-sync/pkg/client/model"
"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/pkg/utils" "github.com/bakito/adguardhome-sync/pkg/utils"
"github.com/go-resty/resty/v2"
"go.uber.org/zap"
) )
const envRedirectPolicyNoOfRedirects = "REDIRECT_POLICY_NO_OF_REDIRECTS" const envRedirectPolicyNoOfRedirects = "REDIRECT_POLICY_NO_OF_REDIRECTS"
var ( var (
l = log.GetLogger("client") l = log.GetLogger("client")
// ErrSetupNeeded custom error // ErrSetupNeeded custom error.
ErrSetupNeeded = errors.New("setup needed") ErrSetupNeeded = errors.New("setup needed")
) )
@@ -33,16 +34,16 @@ func detailedError(resp *resty.Response, err error) error {
e += fmt.Sprintf("(%s)", string(resp.Body())) e += fmt.Sprintf("(%s)", string(resp.Body()))
} }
if err != nil { if err != nil {
e += fmt.Sprintf(": %s", err.Error()) e += ": " + err.Error()
} }
return errors.New(e) return errors.New(e)
} }
// New create a new 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
if config.APIPath == "" { if config.APIPath == "" {
apiURL = fmt.Sprintf("%s/control", config.URL) apiURL = config.URL + "/control"
} else { } else {
apiURL = fmt.Sprintf("%s/%s", config.URL, config.APIPath) apiURL = fmt.Sprintf("%s/%s", config.URL, config.APIPath)
} }
@@ -84,7 +85,9 @@ func New(config types.AdGuardInstance) (Client, error) {
}, nil }, nil
} }
// Client AdguardHome API client interface // Client AdguardHome API client interface.
//
//nolint:interfacebloat
type Client interface { type Client interface {
Host() string Host() string
Status() (*model.ServerStatus, error) Status() (*model.ServerStatus, error)
@@ -116,16 +119,16 @@ type Client interface {
UpdateClient(client *model.Client) error UpdateClient(client *model.Client) error
DeleteClient(client *model.Client) error DeleteClient(client *model.Client) error
QueryLogConfig() (*model.QueryLogConfigWithIgnored, error) QueryLogConfig() (*model.QueryLogConfigWithIgnored, error)
SetQueryLogConfig(*model.QueryLogConfigWithIgnored) error SetQueryLogConfig(ql *model.QueryLogConfigWithIgnored) error
StatsConfig() (*model.GetStatsConfigResponse, error) StatsConfig() (*model.GetStatsConfigResponse, error)
SetStatsConfig(sc *model.PutStatsConfigUpdateRequest) error SetStatsConfig(sc *model.PutStatsConfigUpdateRequest) error
Setup() error Setup() error
AccessList() (*model.AccessList, error) AccessList() (*model.AccessList, error)
SetAccessList(*model.AccessList) error SetAccessList(accessList *model.AccessList) error
DNSConfig() (*model.DNSConfig, error) DNSConfig() (*model.DNSConfig, error)
SetDNSConfig(*model.DNSConfig) error SetDNSConfig(config *model.DNSConfig) error
DhcpConfig() (*model.DhcpStatus, error) DhcpConfig() (*model.DhcpStatus, error)
SetDhcpConfig(*model.DhcpStatus) error SetDhcpConfig(status *model.DhcpStatus) error
AddDHCPStaticLease(lease model.DhcpStaticLease) error AddDHCPStaticLease(lease model.DhcpStaticLease) error
DeleteDHCPStaticLease(lease model.DhcpStaticLease) error DeleteDHCPStaticLease(lease model.DhcpStaticLease) error
} }
@@ -224,7 +227,7 @@ func (cl *client) toggleStatus(mode string) (bool, error) {
} }
func (cl *client) toggleBool(mode string, enable bool) error { func (cl *client) toggleBool(mode string, enable bool) error {
cl.log.With("enable", enable).Info(fmt.Sprintf("Toggle %s", mode)) cl.log.With("enable", enable).Info("Toggle " + mode)
var target string var target string
if enable { if enable {
target = "enable" target = "enable"

View File

@@ -8,13 +8,14 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/google/uuid"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/bakito/adguardhome-sync/pkg/client" "github.com/bakito/adguardhome-sync/pkg/client"
"github.com/bakito/adguardhome-sync/pkg/client/model" "github.com/bakito/adguardhome-sync/pkg/client/model"
"github.com/bakito/adguardhome-sync/pkg/types" "github.com/bakito/adguardhome-sync/pkg/types"
"github.com/bakito/adguardhome-sync/pkg/utils" "github.com/bakito/adguardhome-sync/pkg/utils"
"github.com/google/uuid"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
) )
var ( var (
@@ -140,7 +141,7 @@ var _ = Describe("Client", func() {
ts, cl = ClientPost( ts, cl = ClientPost(
"/install/configure", "/install/configure",
fmt.Sprintf( fmt.Sprintf(
`{"web":{"ip":"0.0.0.0","port":3000,"status":"","can_autofix":false},"dns":{"ip":"0.0.0.0","port":53,"status":"","can_autofix":false},"username":"%s","password":"%s"}`, `{"web":{"ip":"0.0.0.0","port":3000,"status":"","can_autofix":false},"dns":{"ip":"0.0.0.0","port":53,"status":"","can_autofix":false},"username":%q,"password":%q}`,
username, username,
password, password,
), ),
@@ -373,10 +374,10 @@ var _ = Describe("Client", func() {
}) })
}) })
func ClientGet(file string, path string) (*httptest.Server, client.Client) { func ClientGet(file, path string) (*httptest.Server, client.Client) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Ω(r.URL.Path).Should(Equal(types.DefaultAPIPath + path)) Ω(r.URL.Path).Should(Equal(types.DefaultAPIPath + path))
b, err := os.ReadFile(filepath.Join("../../testdata", file)) b, err := os.ReadFile(filepath.Join("..", "..", "testdata", file))
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
_, err = w.Write(b) _, err = w.Write(b)

View File

@@ -11,19 +11,20 @@ import (
"net/url" "net/url"
"path" "path"
"go.uber.org/zap"
"github.com/bakito/adguardhome-sync/pkg/client/model" "github.com/bakito/adguardhome-sync/pkg/client/model"
"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"
"go.uber.org/zap"
) )
var l = log.GetLogger("client") var l = log.GetLogger("client")
// New create a new api client // New create a new api client.
func New(config types.AdGuardInstance) (Client, error) { func New(config types.AdGuardInstance) (Client, error) {
var apiURL string var apiURL string
if config.APIPath == "" { if config.APIPath == "" {
apiURL = fmt.Sprintf("%s/control", config.URL) apiURL = config.URL + "/control"
} else { } else {
apiURL = fmt.Sprintf("%s/%s", config.URL, config.APIPath) apiURL = fmt.Sprintf("%s/%s", config.URL, config.APIPath)
} }
@@ -96,7 +97,7 @@ func (a apiClient) SetFilteringConfig(ctx context.Context, config model.FilterCo
return write(ctx, config, a.client.FilteringConfig) return write(ctx, config, a.client.FilteringConfig)
} }
func write[B interface{}]( func write[B any](
ctx context.Context, ctx context.Context,
body B, body B,
req func(ctx context.Context, body B, reqEditors ...model.RequestEditorFn) (*http.Response, error), req func(ctx context.Context, body B, reqEditors ...model.RequestEditorFn) (*http.Response, error),
@@ -112,7 +113,7 @@ func write[B interface{}](
return nil return nil
} }
func read[I interface{}]( func read[I any](
ctx context.Context, ctx context.Context,
req func(ctx context.Context, reqEditors ...model.RequestEditorFn) (*http.Response, error), req func(ctx context.Context, reqEditors ...model.RequestEditorFn) (*http.Response, error),
parse func(rsp *http.Response) (*I, error), parse func(rsp *http.Response) (*I, error),

View File

@@ -3,8 +3,9 @@ package client
import ( import (
"net/http" "net/http"
"github.com/bakito/adguardhome-sync/pkg/client/model"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
"github.com/bakito/adguardhome-sync/pkg/client/model"
) )
var _ model.HttpRequestDoer = &adapter{} var _ model.HttpRequestDoer = &adapter{}

View File

@@ -5,13 +5,14 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/bakito/adguardhome-sync/pkg/utils"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"go.uber.org/zap" "go.uber.org/zap"
"k8s.io/utils/ptr" "k8s.io/utils/ptr"
"github.com/bakito/adguardhome-sync/pkg/utils"
) )
// Clone the config // Clone the config.
func (c *DhcpStatus) Clone() *DhcpStatus { func (c *DhcpStatus) Clone() *DhcpStatus {
clone := &DhcpStatus{} clone := &DhcpStatus{}
_ = copier.Copy(clone, c) _ = copier.Copy(clone, c)
@@ -27,16 +28,16 @@ func (c *DhcpStatus) cleanV4V6() {
} }
} }
// CleanAndEquals dhcp server config equal check where V4 and V6 are cleaned in advance // CleanAndEquals dhcp server config equal check where V4 and V6 are cleaned in advance.
func (c *DhcpStatus) CleanAndEquals(o *DhcpStatus) bool { func (c *DhcpStatus) CleanAndEquals(o *DhcpStatus) bool {
c.cleanV4V6() c.cleanV4V6()
o.cleanV4V6() o.cleanV4V6()
return c.Equals(o) return c.Equals(o)
} }
// Equals dhcp server config equal check // Equals dhcp server config equal check.
func (c *DhcpStatus) Equals(o *DhcpStatus) bool { func (c *DhcpStatus) Equals(o *DhcpStatus) bool {
return utils.JsonEquals(c, o) return utils.JSONEquals(c, o)
} }
func (c *DhcpStatus) HasConfig() bool { func (c *DhcpStatus) HasConfig() bool {
@@ -56,8 +57,8 @@ func (j DhcpConfigV6) isValid() bool {
type DhcpStaticLeases []DhcpStaticLease type DhcpStaticLeases []DhcpStaticLease
// MergeDhcpStaticLeases the leases // MergeDhcpStaticLeases the leases.
func MergeDhcpStaticLeases(l *[]DhcpStaticLease, other *[]DhcpStaticLease) (DhcpStaticLeases, DhcpStaticLeases) { func MergeDhcpStaticLeases(l, other *[]DhcpStaticLease) (adds, removes DhcpStaticLeases) {
var thisLeases []DhcpStaticLease var thisLeases []DhcpStaticLease
var otherLeases []DhcpStaticLease var otherLeases []DhcpStaticLease
@@ -69,8 +70,6 @@ func MergeDhcpStaticLeases(l *[]DhcpStaticLease, other *[]DhcpStaticLease) (Dhcp
} }
current := make(map[string]DhcpStaticLease) current := make(map[string]DhcpStaticLease)
var adds DhcpStaticLeases
var removes DhcpStaticLeases
for _, le := range thisLeases { for _, le := range thisLeases {
current[le.Mac] = le current[le.Mac] = le
} }
@@ -90,21 +89,21 @@ func MergeDhcpStaticLeases(l *[]DhcpStaticLease, other *[]DhcpStaticLease) (Dhcp
return adds, removes return adds, removes
} }
// Equals dns config equal check // Equals dns config equal check.
func (c *DNSConfig) Equals(o *DNSConfig) bool { func (c *DNSConfig) Equals(o *DNSConfig) bool {
cc := c.Clone() cc := c.Clone()
oo := o.Clone() oo := o.Clone()
cc.Sort() cc.Sort()
oo.Sort() oo.Sort()
return utils.JsonEquals(cc, oo) return utils.JSONEquals(cc, oo)
} }
func (c *DNSConfig) Clone() *DNSConfig { func (c *DNSConfig) Clone() *DNSConfig {
return utils.Clone(c, &DNSConfig{}) return utils.Clone(c, &DNSConfig{})
} }
// Sort sort dns config // Sort dns config.
func (c *DNSConfig) Sort() { func (c *DNSConfig) Sort() {
if c.UpstreamDns != nil { if c.UpstreamDns != nil {
sort.Strings(*c.UpstreamDns) sort.Strings(*c.UpstreamDns)
@@ -119,14 +118,14 @@ func (c *DNSConfig) Sort() {
} }
} }
// Equals access list equal check // Equals access list equal check.
func (al *AccessList) Equals(o *AccessList) bool { func (al *AccessList) Equals(o *AccessList) bool {
return EqualsStringSlice(al.AllowedClients, o.AllowedClients, true) && return EqualsStringSlice(al.AllowedClients, o.AllowedClients, true) &&
EqualsStringSlice(al.DisallowedClients, o.DisallowedClients, true) && EqualsStringSlice(al.DisallowedClients, o.DisallowedClients, true) &&
EqualsStringSlice(al.BlockedHosts, o.BlockedHosts, true) EqualsStringSlice(al.BlockedHosts, o.BlockedHosts, true)
} }
func EqualsStringSlice(a *[]string, b *[]string, sortIt bool) bool { func EqualsStringSlice(a, b *[]string, sortIt bool) bool {
if a == nil && b == nil { if a == nil && b == nil {
return true return true
} }
@@ -152,7 +151,7 @@ func EqualsStringSlice(a *[]string, b *[]string, sortIt bool) bool {
return true return true
} }
// Sort clients // Sort clients.
func (cl *Client) Sort() { func (cl *Client) Sort() {
if cl.Ids != nil { if cl.Ids != nil {
sort.Strings(*cl.Ids) sort.Strings(*cl.Ids)
@@ -168,28 +167,26 @@ func (cl *Client) Sort() {
} }
} }
// PrepareDiff timezone BlockedServicesSchedule might differ if all other fields are empty, // PrepareDiff so we skip it in diff.
// so we skip it in diff
func (cl *Client) PrepareDiff() *string { func (cl *Client) PrepareDiff() *string {
var tz *string var tz *string
bss := cl.BlockedServicesSchedule bss := cl.BlockedServicesSchedule
if bss != nil && bss.Mon == nil && bss.Tue == nil && bss.Wed == nil && if bss != nil && bss.Mon == nil && bss.Tue == nil && bss.Wed == nil &&
bss.Thu == nil && bss.Fri == nil && bss.Sat == nil && bss.Sun == nil { bss.Thu == nil && bss.Fri == nil && bss.Sat == nil && bss.Sun == nil {
tz = cl.BlockedServicesSchedule.TimeZone tz = cl.BlockedServicesSchedule.TimeZone
cl.BlockedServicesSchedule.TimeZone = nil cl.BlockedServicesSchedule.TimeZone = nil
} }
return tz return tz
} }
// AfterDiff reset after diff // AfterDiff reset after diff.
func (cl *Client) AfterDiff(tz *string) { func (cl *Client) AfterDiff(tz *string) {
if cl.BlockedServicesSchedule != nil { if cl.BlockedServicesSchedule != nil {
cl.BlockedServicesSchedule.TimeZone = tz cl.BlockedServicesSchedule.TimeZone = tz
} }
} }
// Equals Clients equal check // Equals Clients equal check.
func (cl *Client) Equals(o *Client) bool { func (cl *Client) Equals(o *Client) bool {
cl.Sort() cl.Sort()
o.Sort() o.Sort()
@@ -202,10 +199,10 @@ func (cl *Client) Equals(o *Client) bool {
o.AfterDiff(bssO) o.AfterDiff(bssO)
}() }()
return utils.JsonEquals(cl, o) return utils.JSONEquals(cl, o)
} }
// Add ac client // Add ac client.
func (clients *Clients) Add(cl Client) { func (clients *Clients) Add(cl Client) {
if clients.Clients == nil { if clients.Clients == nil {
clients.Clients = &ClientsArray{cl} clients.Clients = &ClientsArray{cl}
@@ -215,8 +212,8 @@ func (clients *Clients) Add(cl Client) {
} }
} }
// Merge merge Clients // Merge merge Clients.
func (clients *Clients) Merge(other *Clients) ([]*Client, []*Client, []*Client) { func (clients *Clients) Merge(other *Clients) (adds, removes, updates []*Client) {
current := make(map[string]*Client) current := make(map[string]*Client)
if clients.Clients != nil { if clients.Clients != nil {
cc := *clients.Clients cc := *clients.Clients
@@ -233,10 +230,6 @@ func (clients *Clients) Merge(other *Clients) ([]*Client, []*Client, []*Client)
} }
} }
var adds []*Client
var removes []*Client
var updates []*Client
for _, cl := range expected { for _, cl := range expected {
if oc, ok := current[*cl.Name]; ok { if oc, ok := current[*cl.Name]; ok {
if !cl.Equals(oc) { if !cl.Equals(oc) {
@@ -255,7 +248,7 @@ func (clients *Clients) Merge(other *Clients) ([]*Client, []*Client, []*Client)
return adds, updates, removes return adds, updates, removes
} }
// Key RewriteEntry key // Key RewriteEntry key.
func (re *RewriteEntry) Key() string { func (re *RewriteEntry) Key() string {
var d string var d string
var a string var a string
@@ -268,16 +261,13 @@ func (re *RewriteEntry) Key() string {
return fmt.Sprintf("%s#%s", d, a) return fmt.Sprintf("%s#%s", d, a)
} }
// RewriteEntries list of RewriteEntry // RewriteEntries list of RewriteEntry.
type RewriteEntries []RewriteEntry type RewriteEntries []RewriteEntry
// Merge RewriteEntries // Merge RewriteEntries.
func (rwe *RewriteEntries) Merge(other *RewriteEntries) (RewriteEntries, RewriteEntries, RewriteEntries) { func (rwe *RewriteEntries) Merge(other *RewriteEntries) (adds, removes, duplicates RewriteEntries) {
current := make(map[string]RewriteEntry) current := make(map[string]RewriteEntry)
var adds RewriteEntries
var removes RewriteEntries
var duplicates RewriteEntries
processed := make(map[string]bool) processed := make(map[string]bool)
for _, rr := range *rwe { for _, rr := range *rwe {
if _, ok := processed[rr.Key()]; !ok { if _, ok := processed[rr.Key()]; !ok {
@@ -310,16 +300,13 @@ func (rwe *RewriteEntries) Merge(other *RewriteEntries) (RewriteEntries, Rewrite
return adds, removes, duplicates return adds, removes, duplicates
} }
func MergeFilters(this *[]Filter, other *[]Filter) ([]Filter, []Filter, []Filter) { func MergeFilters(this, other *[]Filter) (adds, updates, removes []Filter) {
if this == nil && other == nil { if this == nil && other == nil {
return nil, nil, nil return nil, nil, nil
} }
current := make(map[string]*Filter) current := make(map[string]*Filter)
var adds []Filter
var updates []Filter
var removes []Filter
if this != nil { if this != nil {
for _, fi := range *this { for _, fi := range *this {
current[fi.Url] = &fi current[fi.Url] = &fi
@@ -346,7 +333,7 @@ func MergeFilters(this *[]Filter, other *[]Filter) ([]Filter, []Filter, []Filter
return adds, updates, removes return adds, updates, removes
} }
// Equals Filter equal check // Equals Filter equal check.
func (f *Filter) Equals(o *Filter) bool { func (f *Filter) Equals(o *Filter) bool {
return f.Enabled == o.Enabled && f.Url == o.Url && f.Name == o.Name return f.Enabled == o.Enabled && f.Url == o.Url && f.Name == o.Name
} }
@@ -358,17 +345,17 @@ type QueryLogConfigWithIgnored struct {
Ignored []string `json:"ignored,omitempty"` Ignored []string `json:"ignored,omitempty"`
} }
// Equals QueryLogConfig equal check // Equals QueryLogConfig equal check.
func (qlc *QueryLogConfigWithIgnored) Equals(o *QueryLogConfigWithIgnored) bool { func (qlc *QueryLogConfigWithIgnored) Equals(o *QueryLogConfigWithIgnored) bool {
return utils.JsonEquals(qlc, o) return utils.JSONEquals(qlc, o)
} }
// Equals QueryLogConfigInterval equal check // Equals QueryLogConfigInterval equal check.
func (qlc *QueryLogConfigInterval) Equals(o *QueryLogConfigInterval) bool { func (qlc *QueryLogConfigInterval) Equals(o *QueryLogConfigInterval) bool {
return ptrEquals(qlc, o) return ptrEquals(qlc, o)
} }
func ptrEquals[T comparable](a *T, b *T) bool { func ptrEquals[T comparable](a, b *T) bool {
if a == nil && b == nil { if a == nil && b == nil {
return true return true
} }
@@ -384,7 +371,7 @@ func ptrEquals[T comparable](a *T, b *T) bool {
return aa == bb return aa == bb
} }
// EnableConfig API struct // EnableConfig API struct.
type EnableConfig struct { type EnableConfig struct {
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
} }
@@ -421,7 +408,7 @@ func (pi *ProfileInfo) ShouldSyncFor(o *ProfileInfo, withTheme bool) *ProfileInf
} }
func (bss *BlockedServicesSchedule) Equals(o *BlockedServicesSchedule) bool { func (bss *BlockedServicesSchedule) Equals(o *BlockedServicesSchedule) bool {
return utils.JsonEquals(bss, o) return utils.JSONEquals(bss, o)
} }
func (bss *BlockedServicesSchedule) ServicesString() string { func (bss *BlockedServicesSchedule) ServicesString() string {
@@ -449,9 +436,9 @@ func (c *DNSConfig) Sanitize(l *zap.SugaredLogger) {
} }
} }
// Equals GetStatsConfigResponse equal check // Equals GetStatsConfigResponse equal check.
func (sc *GetStatsConfigResponse) Equals(o *GetStatsConfigResponse) bool { func (sc *GetStatsConfigResponse) Equals(o *GetStatsConfigResponse) bool {
return utils.JsonEquals(sc, o) return utils.JSONEquals(sc, o)
} }
func NewStats() *Stats { func NewStats() *Stats {
@@ -482,19 +469,19 @@ func (s *Stats) Add(other *Stats) {
s.ReplacedSafebrowsing = sumUp(s.ReplacedSafebrowsing, other.ReplacedSafebrowsing) s.ReplacedSafebrowsing = sumUp(s.ReplacedSafebrowsing, other.ReplacedSafebrowsing)
} }
func addInt(t *int, add *int) *int { func addInt(t, add *int) *int {
if add != nil { if add != nil {
return ptr.To(*t + *add) return ptr.To(*t + *add)
} }
return t return t
} }
func sumUp(t *[]int, o *[]int) *[]int { func sumUp(t, o *[]int) *[]int {
if o != nil { if o != nil {
tt := *t tt := *t
oo := *o oo := *o
var sum []int var sum []int
for i := 0; i < len(tt); i++ { for i := range tt {
if len(oo) >= i { if len(oo) >= i {
sum = append(sum, tt[i]+oo[i]) sum = append(sum, tt[i]+oo[i])
} }

View File

@@ -1,11 +1,12 @@
package model package model
import ( import (
"github.com/bakito/adguardhome-sync/pkg/log"
"github.com/bakito/adguardhome-sync/pkg/utils"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
"github.com/onsi/gomega" "github.com/onsi/gomega"
"go.uber.org/zap" "go.uber.org/zap"
"github.com/bakito/adguardhome-sync/pkg/log"
"github.com/bakito/adguardhome-sync/pkg/utils"
) )
var _ = Describe("Types", func() { var _ = Describe("Types", func() {

View File

@@ -4,12 +4,13 @@ import (
"encoding/json" "encoding/json"
"os" "os"
"github.com/bakito/adguardhome-sync/pkg/client/model"
"github.com/bakito/adguardhome-sync/pkg/types"
"github.com/bakito/adguardhome-sync/pkg/utils"
"github.com/google/uuid" "github.com/google/uuid"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/bakito/adguardhome-sync/pkg/client/model"
"github.com/bakito/adguardhome-sync/pkg/types"
"github.com/bakito/adguardhome-sync/pkg/utils"
) )
var _ = Describe("Types", func() { var _ = Describe("Types", func() {

View File

@@ -4,9 +4,10 @@ import (
"errors" "errors"
"regexp" "regexp"
"github.com/caarlos0/env/v11"
"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/caarlos0/env/v11"
) )
var ( var (
@@ -38,7 +39,7 @@ func Get(configFile string, flags Flags) (*AppConfig, error) {
return nil, err return nil, err
} }
if err = validateSchema(path); err != nil { if err := validateSchema(path); err != nil {
return nil, err return nil, err
} }
@@ -67,10 +68,10 @@ func Get(configFile string, flags Flags) (*AppConfig, error) {
cfg.Replicas = nil cfg.Replicas = nil
// overwrite from env vars // overwrite from env vars
if err = env.Parse(cfg); err != nil { if err := env.Parse(cfg); err != nil {
return nil, err return nil, err
} }
if err = env.ParseWithOptions(cfg.Replica, env.Options{Prefix: "REPLICA_"}); err != nil { if err := env.ParseWithOptions(cfg.Replica, env.Options{Prefix: "REPLICA_"}); err != nil {
return nil, err return nil, err
} }
// restore the replica // restore the replica
@@ -81,7 +82,7 @@ func Get(configFile string, flags Flags) (*AppConfig, error) {
cfg.Replica.DHCPServerEnabled = replicaDhcpServer cfg.Replica.DHCPServerEnabled = replicaDhcpServer
} }
if err = env.ParseWithOptions(&cfg.Origin, env.Options{Prefix: "ORIGIN_"}); err != nil { if err := env.ParseWithOptions(&cfg.Origin, env.Options{Prefix: "ORIGIN_"}); err != nil {
return nil, err return nil, err
} }

View File

@@ -3,11 +3,12 @@ package config_test
import ( import (
"os" "os"
"github.com/bakito/adguardhome-sync/pkg/config"
flagsmock "github.com/bakito/adguardhome-sync/pkg/mocks/flags"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
gm "go.uber.org/mock/gomock" gm "go.uber.org/mock/gomock"
"github.com/bakito/adguardhome-sync/pkg/config"
flagsmock "github.com/bakito/adguardhome-sync/pkg/mocks/flags"
) )
var _ = Describe("Config", func() { var _ = Describe("Config", func() {
@@ -16,7 +17,7 @@ var _ = Describe("Config", func() {
flags *flagsmock.MockFlags flags *flagsmock.MockFlags
mockCtrl *gm.Controller mockCtrl *gm.Controller
changedEnvVars []string changedEnvVars []string
setEnv = func(name string, value string) { setEnv = func(name, value string) {
_ = os.Setenv(name, value) _ = os.Setenv(name, value)
changedEnvVars = append(changedEnvVars, name) changedEnvVars = append(changedEnvVars, name)
} }
@@ -125,9 +126,9 @@ var _ = Describe("Config", func() {
Ω(cfg.Get().API.Port).Should(Equal(9090)) Ω(cfg.Get().API.Port).Should(Equal(9090))
}) })
It("should have the api port from the config flags", func() { It("should have the api port from the config flags", func() {
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes() flags.EXPECT().Changed(config.FlagAPIPort).Return(true).AnyTimes()
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes() flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
flags.EXPECT().GetInt(config.FlagApiPort).Return(9990, nil).AnyTimes() flags.EXPECT().GetInt(config.FlagAPIPort).Return(9990, nil).AnyTimes()
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags) cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())
@@ -135,9 +136,9 @@ var _ = Describe("Config", func() {
}) })
It("should have the api port from the config env var", func() { It("should have the api port from the config env var", func() {
setEnv("API_PORT", "9999") setEnv("API_PORT", "9999")
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes() flags.EXPECT().Changed(config.FlagAPIPort).Return(true).AnyTimes()
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes() flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
flags.EXPECT().GetInt(config.FlagApiPort).Return(9990, nil).AnyTimes() flags.EXPECT().GetInt(config.FlagAPIPort).Return(9990, nil).AnyTimes()
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags) cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())
@@ -174,9 +175,9 @@ var _ = Describe("Config", func() {
Ω(cfg.Get().API.Port).Should(Equal(9090)) Ω(cfg.Get().API.Port).Should(Equal(9090))
}) })
It("should have the api port from the config flags", func() { It("should have the api port from the config flags", func() {
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes() flags.EXPECT().Changed(config.FlagAPIPort).Return(true).AnyTimes()
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes() flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
flags.EXPECT().GetInt(config.FlagApiPort).Return(9990, nil).AnyTimes() flags.EXPECT().GetInt(config.FlagAPIPort).Return(9990, nil).AnyTimes()
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags) cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())
@@ -184,9 +185,9 @@ var _ = Describe("Config", func() {
}) })
It("should have the api port from the config env var", func() { It("should have the api port from the config env var", func() {
setEnv("API_PORT", "9999") setEnv("API_PORT", "9999")
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes() flags.EXPECT().Changed(config.FlagAPIPort).Return(true).AnyTimes()
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes() flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
flags.EXPECT().GetInt(config.FlagApiPort).Return(9990, nil).AnyTimes() flags.EXPECT().GetInt(config.FlagAPIPort).Return(9990, nil).AnyTimes()
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags) cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())
@@ -202,9 +203,9 @@ var _ = Describe("Config", func() {
Ω(cfg.Get().Features.DNS.ServerConfig).Should(BeFalse()) Ω(cfg.Get().Features.DNS.ServerConfig).Should(BeFalse())
}) })
It("should have the feature dns server config from the config flags", func() { It("should have the feature dns server config from the config flags", func() {
flags.EXPECT().Changed(config.FlagFeatureDnsServerConfig).Return(true).AnyTimes() flags.EXPECT().Changed(config.FlagFeatureDNSServerConfig).Return(true).AnyTimes()
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes() flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
flags.EXPECT().GetBool(config.FlagFeatureDnsServerConfig).Return(true, nil).AnyTimes() flags.EXPECT().GetBool(config.FlagFeatureDNSServerConfig).Return(true, nil).AnyTimes()
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags) cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())
@@ -212,9 +213,9 @@ var _ = Describe("Config", func() {
}) })
It("should have the feature dns server config from the config env var", func() { It("should have the feature dns server config from the config env var", func() {
setEnv("FEATURES_DNS_SERVER_CONFIG", "false") setEnv("FEATURES_DNS_SERVER_CONFIG", "false")
flags.EXPECT().Changed(config.FlagFeatureDnsServerConfig).Return(true).AnyTimes() flags.EXPECT().Changed(config.FlagFeatureDNSServerConfig).Return(true).AnyTimes()
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes() flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
flags.EXPECT().GetBool(config.FlagFeatureDnsServerConfig).Return(true, nil).AnyTimes() flags.EXPECT().GetBool(config.FlagFeatureDNSServerConfig).Return(true, nil).AnyTimes()
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags) cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())
@@ -222,9 +223,9 @@ var _ = Describe("Config", func() {
}) })
It("should have the feature dns server config from the config DEPRECATED env var", func() { It("should have the feature dns server config from the config DEPRECATED env var", func() {
setEnv("FEATURES_DNS_SERVERCONFIG", "false") setEnv("FEATURES_DNS_SERVERCONFIG", "false")
flags.EXPECT().Changed(config.FlagFeatureDnsServerConfig).Return(true).AnyTimes() flags.EXPECT().Changed(config.FlagFeatureDNSServerConfig).Return(true).AnyTimes()
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes() flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
flags.EXPECT().GetBool(config.FlagFeatureDnsServerConfig).Return(true, nil).AnyTimes() flags.EXPECT().GetBool(config.FlagFeatureDNSServerConfig).Return(true, nil).AnyTimes()
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags) cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())

View File

@@ -4,9 +4,10 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/bakito/adguardhome-sync/pkg/config"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/bakito/adguardhome-sync/pkg/config"
) )
var envVars = []string{ var envVars = []string{

View File

@@ -6,9 +6,10 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/caarlos0/env/v11"
"github.com/bakito/adguardhome-sync/pkg/types" "github.com/bakito/adguardhome-sync/pkg/types"
"github.com/bakito/adguardhome-sync/pkg/utils" "github.com/bakito/adguardhome-sync/pkg/utils"
"github.com/caarlos0/env/v11"
) )
func handleDeprecatedEnvVars(cfg *types.Config) { func handleDeprecatedEnvVars(cfg *types.Config) {
@@ -61,20 +62,20 @@ func handleDeprecatedEnvVars(cfg *types.Config) {
} }
} }
func checkDeprecatedEnvVar(oldName string, newName string) (string, bool) { func checkDeprecatedEnvVar(oldName, newName string) (string, bool) {
old, oldOK := os.LookupEnv(oldName) old, oldOK := os.LookupEnv(oldName)
if oldOK { if oldOK {
logger.With("deprecated", oldName, "replacement", newName). logger.With("deprecated", oldName, "replacement", newName).
Warn("Deprecated env variable is used, please use the correct one") Warn("Deprecated env variable is used, please use the correct one")
} }
new, newOK := os.LookupEnv(newName) newVal, newOK := os.LookupEnv(newName)
if newOK { if newOK {
return new, true return newVal, true
} }
return old, oldOK return old, oldOK
} }
func checkDeprecatedReplicaEnvVar(oldPattern string, newPattern string, replicaID int) (string, bool) { func checkDeprecatedReplicaEnvVar(oldPattern, newPattern string, replicaID int) (string, bool) {
return checkDeprecatedEnvVar(fmt.Sprintf(oldPattern, replicaID), fmt.Sprintf(newPattern, replicaID)) return checkDeprecatedEnvVar(fmt.Sprintf(oldPattern, replicaID), fmt.Sprintf(newPattern, replicaID))
} }

View File

@@ -4,8 +4,9 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/bakito/adguardhome-sync/pkg/types"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/bakito/adguardhome-sync/pkg/types"
) )
func readFile(cfg *types.Config, path string) (string, error) { func readFile(cfg *types.Config, path string) (string, error) {

View File

@@ -10,9 +10,6 @@ import (
) )
var _ = Describe("Config", func() { var _ = Describe("Config", func() {
var ()
BeforeEach(func() {
})
Context("configFilePath", func() { Context("configFilePath", func() {
It("should return the same value", func() { It("should return the same value", func() {
path := uuid.NewString() path := uuid.NewString()
@@ -26,7 +23,7 @@ var _ = Describe("Config", func() {
result, err := configFilePath("") result, err := configFilePath("")
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())
Ω(result).Should(Equal(filepath.Join(home, "/.adguardhome-sync.yaml"))) Ω(result).Should(Equal(filepath.Join(home, ".adguardhome-sync.yaml")))
}) })
}) })
}) })

View File

@@ -6,16 +6,16 @@ const (
FlagPrintConfigOnly = "printConfigOnly" FlagPrintConfigOnly = "printConfigOnly"
FlagContinueOnError = "continueOnError" FlagContinueOnError = "continueOnError"
FlagApiPort = "api-port" FlagAPIPort = "api-port"
FlagApiUsername = "api-username" FlagAPIUsername = "api-username"
FlagApiPassword = "api-password" FlagAPIPassword = "api-password"
FlagApiDarkMode = "api-dark-mode" FlagAPIDarkMode = "api-dark-mode"
FlagFeatureDhcpServerConfig = "feature-dhcp-server-config" FlagFeatureDhcpServerConfig = "feature-dhcp-server-config"
FlagFeatureDhcpStaticLeases = "feature-dhcp-static-leases" FlagFeatureDhcpStaticLeases = "feature-dhcp-static-leases"
FlagFeatureDnsServerConfig = "feature-dns-server-config" FlagFeatureDNSServerConfig = "feature-dns-server-config"
FlagFeatureDnsAccessLists = "feature-dns-access-lists" FlagFeatureDNSAccessLists = "feature-dns-access-lists"
FlagFeatureDnsRewrites = "feature-dns-rewrites" FlagFeatureDNSRewrites = "feature-dns-rewrites"
FlagFeatureGeneral = "feature-general-settings" FlagFeatureGeneral = "feature-general-settings"
FlagFeatureQueryLog = "feature-query-log-config" FlagFeatureQueryLog = "feature-query-log-config"
FlagFeatureStats = "feature-stats-config" FlagFeatureStats = "feature-stats-config"
@@ -25,7 +25,7 @@ const (
FlagOriginURL = "origin-url" FlagOriginURL = "origin-url"
FlagOriginWebURL = "origin-web-url" FlagOriginWebURL = "origin-web-url"
FlagOriginApiPath = "origin-api-path" FlagOriginAPIPath = "origin-api-path"
FlagOriginUsername = "origin-username" FlagOriginUsername = "origin-username"
FlagOriginPassword = "origin-password" FlagOriginPassword = "origin-password"
@@ -34,7 +34,7 @@ const (
FlagReplicaURL = "replica-url" FlagReplicaURL = "replica-url"
FlagReplicaWebURL = "replica-web-url" FlagReplicaWebURL = "replica-web-url"
FlagReplicaApiPath = "replica-api-path" FlagReplicaAPIPath = "replica-api-path"
FlagReplicaUsername = "replica-username" FlagReplicaUsername = "replica-username"
FlagReplicaPassword = "replica-password" FlagReplicaPassword = "replica-password"
FlagReplicaCookie = "replica-cookie" FlagReplicaCookie = "replica-cookie"

View File

@@ -18,7 +18,7 @@ func readFlags(cfg *types.Config, flags Flags) error {
return err return err
} }
if err := fr.readApiFlags(); err != nil { if err := fr.readAPIFlags(); err != nil {
return err return err
} }
@@ -30,11 +30,7 @@ func readFlags(cfg *types.Config, flags Flags) error {
return err return err
} }
if err := fr.readReplicaFlags(); err != nil { return fr.readReplicaFlags()
return err
}
return nil
} }
type flagReader struct { type flagReader struct {
@@ -53,7 +49,7 @@ func (fr *flagReader) readReplicaFlags() error {
}); err != nil { }); err != nil {
return err return err
} }
if err := fr.setStringFlag(FlagReplicaApiPath, func(cgf *types.Config, value string) { if err := fr.setStringFlag(FlagReplicaAPIPath, func(cgf *types.Config, value string) {
fr.cfg.Replica.APIPath = value fr.cfg.Replica.APIPath = value
}); err != nil { }); err != nil {
return err return err
@@ -83,12 +79,9 @@ func (fr *flagReader) readReplicaFlags() error {
}); err != nil { }); err != nil {
return err return err
} }
if err := fr.setStringFlag(FlagReplicaInterfaceName, func(cgf *types.Config, value string) { return fr.setStringFlag(FlagReplicaInterfaceName, func(cgf *types.Config, value string) {
fr.cfg.Replica.InterfaceName = value fr.cfg.Replica.InterfaceName = value
}); err != nil { })
return err
}
return nil
} }
func (fr *flagReader) readOriginFlags() error { func (fr *flagReader) readOriginFlags() error {
@@ -102,7 +95,7 @@ func (fr *flagReader) readOriginFlags() error {
}); err != nil { }); err != nil {
return err return err
} }
if err := fr.setStringFlag(FlagOriginApiPath, func(cgf *types.Config, value string) { if err := fr.setStringFlag(FlagOriginAPIPath, func(cgf *types.Config, value string) {
fr.cfg.Origin.APIPath = value fr.cfg.Origin.APIPath = value
}); err != nil { }); err != nil {
return err return err
@@ -122,12 +115,9 @@ func (fr *flagReader) readOriginFlags() error {
}); err != nil { }); err != nil {
return err return err
} }
if err := fr.setBoolFlag(FlagOriginISV, func(cgf *types.Config, value bool) { return fr.setBoolFlag(FlagOriginISV, func(cgf *types.Config, value bool) {
fr.cfg.Origin.InsecureSkipVerify = value fr.cfg.Origin.InsecureSkipVerify = value
}); err != nil { })
return err
}
return nil
} }
func (fr *flagReader) readFeatureFlags() error { func (fr *flagReader) readFeatureFlags() error {
@@ -142,17 +132,17 @@ func (fr *flagReader) readFeatureFlags() error {
return err return err
} }
if err := fr.setBoolFlag(FlagFeatureDnsServerConfig, func(cgf *types.Config, value bool) { if err := fr.setBoolFlag(FlagFeatureDNSServerConfig, func(cgf *types.Config, value bool) {
fr.cfg.Features.DNS.ServerConfig = value fr.cfg.Features.DNS.ServerConfig = value
}); err != nil { }); err != nil {
return err return err
} }
if err := fr.setBoolFlag(FlagFeatureDnsAccessLists, func(cgf *types.Config, value bool) { if err := fr.setBoolFlag(FlagFeatureDNSAccessLists, func(cgf *types.Config, value bool) {
fr.cfg.Features.DNS.AccessLists = value fr.cfg.Features.DNS.AccessLists = value
}); err != nil { }); err != nil {
return err return err
} }
if err := fr.setBoolFlag(FlagFeatureDnsRewrites, func(cgf *types.Config, value bool) { if err := fr.setBoolFlag(FlagFeatureDNSRewrites, func(cgf *types.Config, value bool) {
fr.cfg.Features.DNS.Rewrites = value fr.cfg.Features.DNS.Rewrites = value
}); err != nil { }); err != nil {
return err return err
@@ -183,61 +173,51 @@ func (fr *flagReader) readFeatureFlags() error {
}); err != nil { }); err != nil {
return err return err
} }
if err := fr.setBoolFlag(FlagFeatureFilters, func(cgf *types.Config, value bool) { return fr.setBoolFlag(FlagFeatureFilters, func(cgf *types.Config, value bool) {
fr.cfg.Features.Filters = value fr.cfg.Features.Filters = value
})
}
func (fr *flagReader) readAPIFlags() error {
if err := fr.setIntFlag(FlagAPIPort, func(cgf *types.Config, value int) {
fr.cfg.API.Port = value
}); err != nil { }); err != nil {
return err return err
} }
if err := fr.setStringFlag(FlagAPIUsername, func(cgf *types.Config, value string) {
return nil
}
func (fr *flagReader) readApiFlags() (err error) {
if err = fr.setIntFlag(FlagApiPort, func(cgf *types.Config, value int) {
fr.cfg.API.Port = value
}); err != nil {
return
}
if err = fr.setStringFlag(FlagApiUsername, func(cgf *types.Config, value string) {
fr.cfg.API.Username = value fr.cfg.API.Username = value
}); err != nil { }); err != nil {
return return err
} }
if err = fr.setStringFlag(FlagApiPassword, func(cgf *types.Config, value string) { if err := fr.setStringFlag(FlagAPIPassword, func(cgf *types.Config, value string) {
fr.cfg.API.Password = value fr.cfg.API.Password = value
}); err != nil { }); err != nil {
return return err
} }
if err = fr.setBoolFlag(FlagApiDarkMode, func(cgf *types.Config, value bool) { return fr.setBoolFlag(FlagAPIDarkMode, func(cgf *types.Config, value bool) {
fr.cfg.API.DarkMode = value fr.cfg.API.DarkMode = value
}); err != nil { })
return
}
return
} }
func (fr *flagReader) readRootFlags() (err error) { func (fr *flagReader) readRootFlags() error {
if err = fr.setStringFlag(FlagCron, func(cgf *types.Config, value string) { if err := fr.setStringFlag(FlagCron, func(cgf *types.Config, value string) {
fr.cfg.Cron = value fr.cfg.Cron = value
}); err != nil { }); err != nil {
return return err
} }
if err = fr.setBoolFlag(FlagRunOnStart, func(cgf *types.Config, value bool) { if err := fr.setBoolFlag(FlagRunOnStart, func(cgf *types.Config, value bool) {
fr.cfg.RunOnStart = value fr.cfg.RunOnStart = value
}); err != nil { }); err != nil {
return return err
} }
if err = fr.setBoolFlag(FlagPrintConfigOnly, func(cgf *types.Config, value bool) { if err := fr.setBoolFlag(FlagPrintConfigOnly, func(cgf *types.Config, value bool) {
fr.cfg.PrintConfigOnly = value fr.cfg.PrintConfigOnly = value
}); err != nil { }); err != nil {
return return err
} }
if err = fr.setBoolFlag(FlagContinueOnError, func(cgf *types.Config, value bool) { return fr.setBoolFlag(FlagContinueOnError, func(cgf *types.Config, value bool) {
fr.cfg.ContinueOnError = value fr.cfg.ContinueOnError = value
}); err != nil { })
return
}
return
} }
type Flags interface { type Flags interface {
@@ -249,33 +229,33 @@ type Flags interface {
func (fr *flagReader) setStringFlag(name string, cb callback[string]) (err error) { func (fr *flagReader) setStringFlag(name string, cb callback[string]) (err error) {
if fr.flags.Changed(name) { if fr.flags.Changed(name) {
if value, err := fr.flags.GetString(name); err != nil { var value string
if value, err = fr.flags.GetString(name); err != nil {
return err return err
} else {
cb(fr.cfg, value)
} }
cb(fr.cfg, value)
} }
return nil return nil
} }
func (fr *flagReader) setBoolFlag(name string, cb callback[bool]) (err error) { func (fr *flagReader) setBoolFlag(name string, cb callback[bool]) (err error) {
if fr.flags.Changed(name) { if fr.flags.Changed(name) {
if value, err := fr.flags.GetBool(name); err != nil { var value bool
if value, err = fr.flags.GetBool(name); err != nil {
return err return err
} else {
cb(fr.cfg, value)
} }
cb(fr.cfg, value)
} }
return nil return nil
} }
func (fr *flagReader) setIntFlag(name string, cb callback[int]) (err error) { func (fr *flagReader) setIntFlag(name string, cb callback[int]) (err error) {
if fr.flags.Changed(name) { if fr.flags.Changed(name) {
if value, err := fr.flags.GetInt(name); err != nil { var value int
if value, err = fr.flags.GetInt(name); err != nil {
return err return err
} else {
cb(fr.cfg, value)
} }
cb(fr.cfg, value)
} }
return nil return nil
} }

View File

@@ -3,11 +3,12 @@ package config
import ( import (
"strings" "strings"
flagsmock "github.com/bakito/adguardhome-sync/pkg/mocks/flags"
"github.com/bakito/adguardhome-sync/pkg/types"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
gm "go.uber.org/mock/gomock" gm "go.uber.org/mock/gomock"
flagsmock "github.com/bakito/adguardhome-sync/pkg/mocks/flags"
"github.com/bakito/adguardhome-sync/pkg/types"
) )
var _ = Describe("Config", func() { var _ = Describe("Config", func() {
@@ -88,7 +89,7 @@ var _ = Describe("Config", func() {
})) }))
}) })
}) })
Context("readApiFlags", func() { Context("readAPIFlags", func() {
It("should change all values", func() { It("should change all values", func() {
cfg.API = types.API{ cfg.API = types.API{
Port: 1111, Port: 1111,
@@ -99,10 +100,10 @@ var _ = Describe("Config", func() {
flags.EXPECT().Changed(gm.Any()).DoAndReturn(func(name string) bool { flags.EXPECT().Changed(gm.Any()).DoAndReturn(func(name string) bool {
return strings.HasPrefix(name, "api") return strings.HasPrefix(name, "api")
}).AnyTimes() }).AnyTimes()
flags.EXPECT().GetInt(FlagApiPort).Return(9999, nil) flags.EXPECT().GetInt(FlagAPIPort).Return(9999, nil)
flags.EXPECT().GetString(FlagApiUsername).Return("aaaa", nil) flags.EXPECT().GetString(FlagAPIUsername).Return("aaaa", nil)
flags.EXPECT().GetString(FlagApiPassword).Return("bbbb", nil) flags.EXPECT().GetString(FlagAPIPassword).Return("bbbb", nil)
flags.EXPECT().GetBool(FlagApiDarkMode).Return(true, nil) flags.EXPECT().GetBool(FlagAPIDarkMode).Return(true, nil)
err := readFlags(cfg, flags) err := readFlags(cfg, flags)
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())
@@ -154,7 +155,7 @@ var _ = Describe("Config", func() {
flags.EXPECT().Changed(FlagOriginURL).Return(true) flags.EXPECT().Changed(FlagOriginURL).Return(true)
flags.EXPECT().Changed(FlagOriginWebURL).Return(true) flags.EXPECT().Changed(FlagOriginWebURL).Return(true)
flags.EXPECT().Changed(FlagOriginApiPath).Return(true) flags.EXPECT().Changed(FlagOriginAPIPath).Return(true)
flags.EXPECT().Changed(FlagOriginUsername).Return(true) flags.EXPECT().Changed(FlagOriginUsername).Return(true)
flags.EXPECT().Changed(FlagOriginPassword).Return(true) flags.EXPECT().Changed(FlagOriginPassword).Return(true)
flags.EXPECT().Changed(FlagOriginCookie).Return(true) flags.EXPECT().Changed(FlagOriginCookie).Return(true)
@@ -163,7 +164,7 @@ var _ = Describe("Config", func() {
flags.EXPECT().GetString(FlagOriginURL).Return("a", nil) flags.EXPECT().GetString(FlagOriginURL).Return("a", nil)
flags.EXPECT().GetString(FlagOriginWebURL).Return("b", nil) flags.EXPECT().GetString(FlagOriginWebURL).Return("b", nil)
flags.EXPECT().GetString(FlagOriginApiPath).Return("c", nil) flags.EXPECT().GetString(FlagOriginAPIPath).Return("c", nil)
flags.EXPECT().GetString(FlagOriginUsername).Return("d", nil) flags.EXPECT().GetString(FlagOriginUsername).Return("d", nil)
flags.EXPECT().GetString(FlagOriginPassword).Return("e", nil) flags.EXPECT().GetString(FlagOriginPassword).Return("e", nil)
flags.EXPECT().GetString(FlagOriginCookie).Return("f", nil) flags.EXPECT().GetString(FlagOriginCookie).Return("f", nil)
@@ -198,7 +199,7 @@ var _ = Describe("Config", func() {
flags.EXPECT().Changed(FlagReplicaURL).Return(true) flags.EXPECT().Changed(FlagReplicaURL).Return(true)
flags.EXPECT().Changed(FlagReplicaWebURL).Return(true) flags.EXPECT().Changed(FlagReplicaWebURL).Return(true)
flags.EXPECT().Changed(FlagReplicaApiPath).Return(true) flags.EXPECT().Changed(FlagReplicaAPIPath).Return(true)
flags.EXPECT().Changed(FlagReplicaUsername).Return(true) flags.EXPECT().Changed(FlagReplicaUsername).Return(true)
flags.EXPECT().Changed(FlagReplicaPassword).Return(true) flags.EXPECT().Changed(FlagReplicaPassword).Return(true)
flags.EXPECT().Changed(FlagReplicaCookie).Return(true) flags.EXPECT().Changed(FlagReplicaCookie).Return(true)
@@ -209,7 +210,7 @@ var _ = Describe("Config", func() {
flags.EXPECT().GetString(FlagReplicaURL).Return("a", nil) flags.EXPECT().GetString(FlagReplicaURL).Return("a", nil)
flags.EXPECT().GetString(FlagReplicaWebURL).Return("b", nil) flags.EXPECT().GetString(FlagReplicaWebURL).Return("b", nil)
flags.EXPECT().GetString(FlagReplicaApiPath).Return("c", nil) flags.EXPECT().GetString(FlagReplicaAPIPath).Return("c", nil)
flags.EXPECT().GetString(FlagReplicaUsername).Return("d", nil) flags.EXPECT().GetString(FlagReplicaUsername).Return("d", nil)
flags.EXPECT().GetString(FlagReplicaPassword).Return("e", nil) flags.EXPECT().GetString(FlagReplicaPassword).Return("e", nil)
flags.EXPECT().GetString(FlagReplicaCookie).Return("f", nil) flags.EXPECT().GetString(FlagReplicaCookie).Return("f", nil)

View File

@@ -9,10 +9,11 @@ import (
"strings" "strings"
"text/template" "text/template"
"gopkg.in/yaml.v3"
"github.com/bakito/adguardhome-sync/pkg/client" "github.com/bakito/adguardhome-sync/pkg/client"
"github.com/bakito/adguardhome-sync/pkg/types" "github.com/bakito/adguardhome-sync/pkg/types"
"github.com/bakito/adguardhome-sync/version" "github.com/bakito/adguardhome-sync/version"
"gopkg.in/yaml.v3"
) )
//go:embed print-config.md //go:embed print-config.md
@@ -25,7 +26,7 @@ func (ac *AppConfig) Print() error {
replicaVersions = append(replicaVersions, aghVersion(replica)) replicaVersions = append(replicaVersions, aghVersion(replica))
} }
out, err := ac.print(os.Environ(), originVersion, replicaVersions) out, err := ac.printInternal(os.Environ(), originVersion, replicaVersions)
if err != nil { if err != nil {
return err return err
} }
@@ -50,7 +51,7 @@ func aghVersion(i types.AdGuardInstance) string {
return stats.Version return stats.Version
} }
func (ac *AppConfig) print(env []string, originVersion string, replicaVersions []string) (string, error) { func (ac *AppConfig) printInternal(env []string, originVersion string, replicaVersions []string) (string, error) {
config, err := yaml.Marshal(ac.Get()) config, err := yaml.Marshal(ac.Get())
if err != nil { if err != nil {
return "", err return "", err
@@ -72,7 +73,7 @@ func (ac *AppConfig) print(env []string, originVersion string, replicaVersions [
var buf bytes.Buffer var buf bytes.Buffer
if err = t.Execute(&buf, map[string]interface{}{ err = t.Execute(&buf, map[string]any{
"Version": version.Version, "Version": version.Version,
"Build": version.Build, "Build": version.Build,
"OperatingSystem": runtime.GOOS, "OperatingSystem": runtime.GOOS,
@@ -83,8 +84,6 @@ func (ac *AppConfig) print(env []string, originVersion string, replicaVersions [
"EnvironmentVariables": strings.Join(env, "\n"), "EnvironmentVariables": strings.Join(env, "\n"),
"OriginVersion": originVersion, "OriginVersion": originVersion,
"ReplicaVersions": replicaVersions, "ReplicaVersions": replicaVersions,
}); err != nil { })
return "", err return buf.String(), err
}
return buf.String(), nil
} }

View File

@@ -7,10 +7,11 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"github.com/bakito/adguardhome-sync/pkg/types"
"github.com/bakito/adguardhome-sync/version"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/bakito/adguardhome-sync/pkg/types"
"github.com/bakito/adguardhome-sync/version"
) )
var _ = Describe("AppConfig", func() { var _ = Describe("AppConfig", func() {
@@ -32,17 +33,17 @@ origin:
} }
env = []string{"FOO=foo", "BAR=bar"} env = []string{"FOO=foo", "BAR=bar"}
}) })
Context("print", func() { Context("printInternal", func() {
It("should print config without file", func() { It("should printInternal config without file", func() {
out, err := ac.print(env, "v0.0.1", []string{"v0.0.2"}) out, err := ac.printInternal(env, "v0.0.1", []string{"v0.0.2"})
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())
Ω( Ω(
out, out,
).Should(Equal(fmt.Sprintf(expected(1), version.Version, version.Build, runtime.GOOS, runtime.GOARCH))) ).Should(Equal(fmt.Sprintf(expected(1), version.Version, version.Build, runtime.GOOS, runtime.GOARCH)))
}) })
It("should print config with file", func() { It("should printInternal config with file", func() {
ac.filePath = "config.yaml" ac.filePath = "config.yaml"
out, err := ac.print(env, "v0.0.1", []string{"v0.0.2"}) out, err := ac.printInternal(env, "v0.0.1", []string{"v0.0.2"})
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())
Ω( Ω(
out, out,
@@ -52,7 +53,9 @@ origin:
}) })
func expected(id int) string { func expected(id int) string {
b, err := os.ReadFile(filepath.Join("../../testdata/config", fmt.Sprintf("print-config_test_expected%d.md", id))) b, err := os.ReadFile(
filepath.Join("..", "..", "testdata", "config", fmt.Sprintf("print-config_test_expected%d.md", id)),
)
Ω(err).ShouldNot(HaveOccurred()) Ω(err).ShouldNot(HaveOccurred())
return string(b) return string(b)
} }

View File

@@ -17,6 +17,7 @@ var schemaData string
func validateSchema(cfgFile string) error { func validateSchema(cfgFile string) error {
// ignore if file not exists // ignore if file not exists
if _, err := os.Stat(cfgFile); err != nil { if _, err := os.Stat(cfgFile); err != nil {
//nolint:nilerr
return nil return nil
} }
// Load YAML file // Load YAML file
@@ -30,7 +31,7 @@ func validateSchema(cfgFile string) error {
func validateYAML(yamlContent []byte) error { func validateYAML(yamlContent []byte) error {
// Convert YAML to JSON // Convert YAML to JSON
var yamlData interface{} var yamlData any
err := yaml.Unmarshal(yamlContent, &yamlData) err := yaml.Unmarshal(yamlContent, &yamlData)
if err != nil { if err != nil {
return err return err

View File

@@ -1,11 +1,12 @@
package config package config
import ( import (
"github.com/bakito/adguardhome-sync/pkg/types"
"github.com/go-faker/faker/v4" "github.com/go-faker/faker/v4"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/bakito/adguardhome-sync/pkg/types"
) )
var _ = Describe("Config", func() { var _ = Describe("Config", func() {

View File

@@ -18,7 +18,7 @@ var (
logs []string logs []string
) )
// GetLogger returns a named logger // GetLogger returns a named logger.
func GetLogger(name string) *zap.SugaredLogger { func GetLogger(name string) *zap.SugaredLogger {
return rootLogger.Named(name).Sugar() return rootLogger.Named(name).Sugar()
} }
@@ -101,12 +101,12 @@ func (l *logList) Sync() error {
return nil return nil
} }
// Logs get the current logs // Logs get the current logs.
func Logs() []string { func Logs() []string {
return logs return logs
} }
// Clear the current logs // Clear the current logs.
func Clear() { func Clear() {
logs = nil logs = nil
} }

View File

@@ -1,9 +1,10 @@
package metrics package metrics
import ( import (
"github.com/prometheus/client_golang/prometheus"
"github.com/bakito/adguardhome-sync/pkg/client/model" "github.com/bakito/adguardhome-sync/pkg/client/model"
"github.com/bakito/adguardhome-sync/pkg/log" "github.com/bakito/adguardhome-sync/pkg/log"
"github.com/prometheus/client_golang/prometheus"
) )
const StatsTotal = "total" const StatsTotal = "total"
@@ -11,7 +12,7 @@ const StatsTotal = "total"
var ( var (
l = log.GetLogger("metrics") l = log.GetLogger("metrics")
// avgProcessingTime - Average processing time for a DNS query // avgProcessingTime - Average processing time for a DNS query.
avgProcessingTime = prometheus.NewGaugeVec( avgProcessingTime = prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "avg_processing_time", Name: "avg_processing_time",
@@ -21,7 +22,7 @@ var (
[]string{"hostname"}, []string{"hostname"},
) )
// dnsQueries - Number of DNS queries // dnsQueries - Number of DNS queries.
dnsQueries = prometheus.NewGaugeVec( dnsQueries = prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "num_dns_queries", Name: "num_dns_queries",
@@ -31,7 +32,7 @@ var (
[]string{"hostname"}, []string{"hostname"},
) )
// blockedFiltering - Number of DNS queries blocked // blockedFiltering - Number of DNS queries blocked.
blockedFiltering = prometheus.NewGaugeVec( blockedFiltering = prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "num_blocked_filtering", Name: "num_blocked_filtering",
@@ -41,7 +42,7 @@ var (
[]string{"hostname"}, []string{"hostname"},
) )
// parentalFiltering - Number of DNS queries replaced by parental control // parentalFiltering - Number of DNS queries replaced by parental control.
parentalFiltering = prometheus.NewGaugeVec( parentalFiltering = prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "num_replaced_parental", Name: "num_replaced_parental",
@@ -51,7 +52,7 @@ var (
[]string{"hostname"}, []string{"hostname"},
) )
// safeBrowsingFiltering - Number of DNS queries replaced by safe browsing // safeBrowsingFiltering - Number of DNS queries replaced by safe browsing.
safeBrowsingFiltering = prometheus.NewGaugeVec( safeBrowsingFiltering = prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "num_replaced_safebrowsing", Name: "num_replaced_safebrowsing",
@@ -61,7 +62,7 @@ var (
[]string{"hostname"}, []string{"hostname"},
) )
// safeSearchFiltering - Number of DNS queries replaced by safe search // safeSearchFiltering - Number of DNS queries replaced by safe search.
safeSearchFiltering = prometheus.NewGaugeVec( safeSearchFiltering = prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "num_replaced_safesearch", Name: "num_replaced_safesearch",
@@ -71,7 +72,7 @@ var (
[]string{"hostname"}, []string{"hostname"},
) )
// topQueries - The number of top queries // topQueries - The number of top queries.
topQueries = prometheus.NewGaugeVec( topQueries = prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "top_queried_domains", Name: "top_queried_domains",
@@ -81,7 +82,7 @@ var (
[]string{"hostname", "domain"}, []string{"hostname", "domain"},
) )
// topBlocked - The number of top domains blocked // topBlocked - The number of top domains blocked.
topBlocked = prometheus.NewGaugeVec( topBlocked = prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "top_blocked_domains", Name: "top_blocked_domains",
@@ -91,7 +92,7 @@ var (
[]string{"hostname", "domain"}, []string{"hostname", "domain"},
) )
// topClients - The number of top clients // topClients - The number of top clients.
topClients = prometheus.NewGaugeVec( topClients = prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "top_clients", Name: "top_clients",
@@ -111,7 +112,7 @@ var (
[]string{"hostname", "type"}, []string{"hostname", "type"},
) )
// running - If Adguard is running // running - If Adguard is running.
running = prometheus.NewGaugeVec( running = prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "running", Name: "running",
@@ -121,7 +122,7 @@ var (
[]string{"hostname"}, []string{"hostname"},
) )
// protectionEnabled - If Adguard protection is enabled // protectionEnabled - If Adguard protection is enabled.
protectionEnabled = prometheus.NewGaugeVec( protectionEnabled = prometheus.NewGaugeVec(
prometheus.GaugeOpts{ prometheus.GaugeOpts{
Name: "protection_enabled", Name: "protection_enabled",
@@ -157,22 +158,22 @@ func initMetric(name string, metric *prometheus.GaugeVec) {
func Update(iml InstanceMetricsList) { func Update(iml InstanceMetricsList) {
for _, im := range iml.Metrics { for _, im := range iml.Metrics {
update(im) updateMetrics(im)
stats[im.HostName] = im.Stats stats[im.HostName] = im.Stats
} }
l.Debug("updated") l.Debug("updated")
} }
func update(im InstanceMetrics) { func updateMetrics(im InstanceMetrics) {
// Status // Status
var isRunning int = 0 isRunning := 0
if im.Status.Running { if im.Status.Running {
isRunning = 1 isRunning = 1
} }
running.WithLabelValues(im.HostName).Set(float64(isRunning)) running.WithLabelValues(im.HostName).Set(float64(isRunning))
var isProtected int = 0 isProtected := 0
if im.Status.ProtectionEnabled { if im.Status.ProtectionEnabled {
isProtected = 1 isProtected = 1
} }
@@ -218,7 +219,7 @@ func update(im InstanceMetrics) {
if len(dnsanswer) > 0 { if len(dnsanswer) > 0 {
for _, dnsa := range dnsanswer { for _, dnsa := range dnsanswer {
dnsType := *dnsa.Type dnsType := *dnsa.Type
m[dnsType] += 1 m[dnsType]++
} }
} }
} }

View File

@@ -1,11 +1,12 @@
package metrics package metrics
import ( import (
"github.com/bakito/adguardhome-sync/pkg/client/model"
"github.com/go-faker/faker/v4" "github.com/go-faker/faker/v4"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"k8s.io/utils/ptr" "k8s.io/utils/ptr"
"github.com/bakito/adguardhome-sync/pkg/client/model"
) )
var _ = Describe("Metrics", func() { var _ = Describe("Metrics", func() {
@@ -85,15 +86,15 @@ var _ = Describe("Metrics", func() {
}) })
}) })
func verifyStats(lines []line) { func verifyStats(lines []Line) {
var total line var total Line
sum := make([]int, len(lines[0].Data)) sum := make([]int, len(lines[0].Data))
for _, l := range lines { for _, l := range lines {
if l.Title == labelTotal { if l.Title == labelTotal {
total = l total = l
} else { } else {
for i, d := range l.Data { for i, d := range l.Data {
sum[i] = sum[i] + d sum[i] += d
} }
} }
} }

View File

@@ -47,19 +47,19 @@ var (
} }
) )
func StatsGraph() (*model.Stats, []line, []line, []line, []line) { func StatsGraph() (t *model.Stats, dns, blocked, malware, adult []Line) {
s := getStats() s := getStats()
t := s.Total() t = s.Total()
dns := graphLines(t, s, blue, blueAlternatives, func(s *model.Stats) []int { dns = graphLines(t, s, blue, blueAlternatives, func(s *model.Stats) []int {
return safeStats(s.DnsQueries) return safeStats(s.DnsQueries)
}) })
blocked := graphLines(t, s, red, redAlternatives, func(s *model.Stats) []int { blocked = graphLines(t, s, red, redAlternatives, func(s *model.Stats) []int {
return safeStats(s.BlockedFiltering) return safeStats(s.BlockedFiltering)
}) })
malware := graphLines(t, s, green, greenAlternatives, func(s *model.Stats) []int { malware = graphLines(t, s, green, greenAlternatives, func(s *model.Stats) []int {
return safeStats(s.ReplacedSafebrowsing) return safeStats(s.ReplacedSafebrowsing)
}) })
adult := graphLines(t, s, yellow, yellowAlternatives, func(s *model.Stats) []int { adult = graphLines(t, s, yellow, yellowAlternatives, func(s *model.Stats) []int {
return safeStats(s.ReplacedParental) return safeStats(s.ReplacedParental)
}) })
@@ -79,9 +79,9 @@ func graphLines(
baseColor []int, baseColor []int,
altColors [][]int, altColors [][]int,
dataCB func(s *model.Stats) []int, dataCB func(s *model.Stats) []int,
) []line { ) []Line {
g := &graph{ g := &graph{
total: line{ total: Line{
Fill: true, Fill: true,
Title: labelTotal, Title: labelTotal,
Data: dataCB(t), Data: dataCB(t),
@@ -94,7 +94,7 @@ func graphLines(
var i int var i int
for name, data := range s { for name, data := range s {
if name != StatsTotal { if name != StatsTotal {
g.replicas = append(g.replicas, line{ g.replicas = append(g.replicas, Line{
Fill: false, Fill: false,
Title: name, Title: name,
Data: dataCB(data), Data: dataCB(data),
@@ -106,9 +106,9 @@ func graphLines(
} }
} }
lines := []line{g.total} lines := []Line{g.total}
slices.SortFunc(g.replicas, func(a, b line) int { slices.SortFunc(g.replicas, func(a, b Line) int {
return strings.Compare(a.Title, b.Title) return strings.Compare(a.Title, b.Title)
}) })
lines = append(lines, g.replicas...) lines = append(lines, g.replicas...)
@@ -116,11 +116,11 @@ func graphLines(
} }
type graph struct { type graph struct {
total line total Line
replicas []line replicas []Line
} }
type line struct { type Line struct {
Data []int `json:"data"` Data []int `json:"data"`
R int `json:"r"` R int `json:"r"`
G int `json:"g"` G int `json:"g"`

View File

@@ -384,17 +384,17 @@ func (mr *MockClientMockRecorder) SafeSearchConfig() *gomock.Call {
} }
// SetAccessList mocks base method. // SetAccessList mocks base method.
func (m *MockClient) SetAccessList(arg0 *model.AccessList) error { func (m *MockClient) SetAccessList(accessList *model.AccessList) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetAccessList", arg0) ret := m.ctrl.Call(m, "SetAccessList", accessList)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// SetAccessList indicates an expected call of SetAccessList. // SetAccessList indicates an expected call of SetAccessList.
func (mr *MockClientMockRecorder) SetAccessList(arg0 any) *gomock.Call { func (mr *MockClientMockRecorder) SetAccessList(accessList any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAccessList", reflect.TypeOf((*MockClient)(nil).SetAccessList), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAccessList", reflect.TypeOf((*MockClient)(nil).SetAccessList), accessList)
} }
// SetBlockedServicesSchedule mocks base method. // SetBlockedServicesSchedule mocks base method.
@@ -426,31 +426,31 @@ func (mr *MockClientMockRecorder) SetCustomRules(rules any) *gomock.Call {
} }
// SetDNSConfig mocks base method. // SetDNSConfig mocks base method.
func (m *MockClient) SetDNSConfig(arg0 *model.DNSConfig) error { func (m *MockClient) SetDNSConfig(config *model.DNSConfig) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetDNSConfig", arg0) ret := m.ctrl.Call(m, "SetDNSConfig", config)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// SetDNSConfig indicates an expected call of SetDNSConfig. // SetDNSConfig indicates an expected call of SetDNSConfig.
func (mr *MockClientMockRecorder) SetDNSConfig(arg0 any) *gomock.Call { func (mr *MockClientMockRecorder) SetDNSConfig(config any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDNSConfig", reflect.TypeOf((*MockClient)(nil).SetDNSConfig), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDNSConfig", reflect.TypeOf((*MockClient)(nil).SetDNSConfig), config)
} }
// SetDhcpConfig mocks base method. // SetDhcpConfig mocks base method.
func (m *MockClient) SetDhcpConfig(arg0 *model.DhcpStatus) error { func (m *MockClient) SetDhcpConfig(status *model.DhcpStatus) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetDhcpConfig", arg0) ret := m.ctrl.Call(m, "SetDhcpConfig", status)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// SetDhcpConfig indicates an expected call of SetDhcpConfig. // SetDhcpConfig indicates an expected call of SetDhcpConfig.
func (mr *MockClientMockRecorder) SetDhcpConfig(arg0 any) *gomock.Call { func (mr *MockClientMockRecorder) SetDhcpConfig(status any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDhcpConfig", reflect.TypeOf((*MockClient)(nil).SetDhcpConfig), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDhcpConfig", reflect.TypeOf((*MockClient)(nil).SetDhcpConfig), status)
} }
// SetProfileInfo mocks base method. // SetProfileInfo mocks base method.
@@ -468,17 +468,17 @@ func (mr *MockClientMockRecorder) SetProfileInfo(settings any) *gomock.Call {
} }
// SetQueryLogConfig mocks base method. // SetQueryLogConfig mocks base method.
func (m *MockClient) SetQueryLogConfig(arg0 *model.QueryLogConfigWithIgnored) error { func (m *MockClient) SetQueryLogConfig(ql *model.QueryLogConfigWithIgnored) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetQueryLogConfig", arg0) ret := m.ctrl.Call(m, "SetQueryLogConfig", ql)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// SetQueryLogConfig indicates an expected call of SetQueryLogConfig. // SetQueryLogConfig indicates an expected call of SetQueryLogConfig.
func (mr *MockClientMockRecorder) SetQueryLogConfig(arg0 any) *gomock.Call { func (mr *MockClientMockRecorder) SetQueryLogConfig(ql any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetQueryLogConfig", reflect.TypeOf((*MockClient)(nil).SetQueryLogConfig), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetQueryLogConfig", reflect.TypeOf((*MockClient)(nil).SetQueryLogConfig), ql)
} }
// SetSafeSearchConfig mocks base method. // SetSafeSearchConfig mocks base method.

View File

@@ -1,10 +1,11 @@
package sync package sync
import ( import (
"go.uber.org/zap"
"github.com/bakito/adguardhome-sync/pkg/client" "github.com/bakito/adguardhome-sync/pkg/client"
"github.com/bakito/adguardhome-sync/pkg/client/model" "github.com/bakito/adguardhome-sync/pkg/client/model"
"github.com/bakito/adguardhome-sync/pkg/utils" "github.com/bakito/adguardhome-sync/pkg/utils"
"go.uber.org/zap"
) )
var ( var (

View File

@@ -1,10 +1,11 @@
package sync package sync
import ( import (
"go.uber.org/zap"
"github.com/bakito/adguardhome-sync/pkg/client" "github.com/bakito/adguardhome-sync/pkg/client"
"github.com/bakito/adguardhome-sync/pkg/client/model" "github.com/bakito/adguardhome-sync/pkg/client/model"
"github.com/bakito/adguardhome-sync/pkg/types" "github.com/bakito/adguardhome-sync/pkg/types"
"go.uber.org/zap"
) )
func setupActions(cfg *types.Config) (actions []syncAction) { func setupActions(cfg *types.Config) (actions []syncAction) {

View File

@@ -14,10 +14,11 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/gin-gonic/gin"
"github.com/bakito/adguardhome-sync/pkg/log" "github.com/bakito/adguardhome-sync/pkg/log"
"github.com/bakito/adguardhome-sync/pkg/metrics" "github.com/bakito/adguardhome-sync/pkg/metrics"
"github.com/bakito/adguardhome-sync/version" "github.com/bakito/adguardhome-sync/version"
"github.com/gin-gonic/gin"
) )
var ( var (
@@ -35,13 +36,13 @@ func (w *worker) handleSync(c *gin.Context) {
func (w *worker) handleRoot(c *gin.Context) { func (w *worker) handleRoot(c *gin.Context) {
total, dns, blocked, malware, adult := metrics.StatsGraph() total, dns, blocked, malware, adult := metrics.StatsGraph()
c.HTML(http.StatusOK, "index.html", map[string]interface{}{ c.HTML(http.StatusOK, "index.html", map[string]any{
"DarkMode": w.cfg.API.DarkMode, "DarkMode": w.cfg.API.DarkMode,
"Metrics": w.cfg.API.Metrics.Enabled, "Metrics": w.cfg.API.Metrics.Enabled,
"Version": version.Version, "Version": version.Version,
"Build": version.Build, "Build": version.Build,
"SyncStatus": w.status(), "SyncStatus": w.status(),
"Stats": map[string]interface{}{ "Stats": map[string]any{
"Labels": getLast24Hours(), "Labels": getLast24Hours(),
"DNS": dns, "DNS": dns,
"Blocked": blocked, "Blocked": blocked,
@@ -169,8 +170,6 @@ func (w *worker) listenAndServe() {
// manually cancel context if not using httpServer.RegisterOnShutdown(cancel) // manually cancel context if not using httpServer.RegisterOnShutdown(cancel)
cancel() cancel()
defer os.Exit(0)
} }
type syncStatus struct { type syncStatus struct {
@@ -192,7 +191,7 @@ func getLast24Hours() []string {
currentTime := time.Now() currentTime := time.Now()
// Loop to get the last 24 hours // Loop to get the last 24 hours
for i := 0; i < 24; i++ { for i := range 24 {
// Calculate the time for the current hour in the loop // Calculate the time for the current hour in the loop
timeInstance := currentTime.Add(time.Duration(-i) * time.Hour) timeInstance := currentTime.Add(time.Duration(-i) * time.Hour)
timeInstance = timeInstance.Truncate(time.Hour) timeInstance = timeInstance.Truncate(time.Hour)

View File

@@ -35,16 +35,17 @@ func (w *worker) scrape() {
metrics.Update(iml) metrics.Update(iml)
} }
func (w *worker) getMetrics(inst types.AdGuardInstance) (im metrics.InstanceMetrics) { func (w *worker) getMetrics(inst types.AdGuardInstance) metrics.InstanceMetrics {
var im metrics.InstanceMetrics
client, err := w.createClient(inst) client, err := w.createClient(inst)
if err != nil { if err != nil {
l.With("error", err, "url", w.cfg.Origin.URL).Error("Error creating origin client") l.With("error", err, "url", w.cfg.Origin.URL).Error("Error creating origin client")
return return im
} }
im.HostName = inst.Host im.HostName = inst.Host
im.Status, _ = client.Status() im.Status, _ = client.Status()
im.Stats, _ = client.Stats() im.Stats, _ = client.Stats()
im.QueryLog, _ = client.QueryLog(w.cfg.API.Metrics.QueryLogLimit) im.QueryLog, _ = client.QueryLog(w.cfg.API.Metrics.QueryLogLimit)
return return im
} }

View File

@@ -2,11 +2,13 @@ package sync
import ( import (
"errors" "errors"
"fmt"
"runtime" "runtime"
"sort" "sort"
"time" "time"
"github.com/robfig/cron/v3"
"go.uber.org/zap"
"github.com/bakito/adguardhome-sync/pkg/client" "github.com/bakito/adguardhome-sync/pkg/client"
"github.com/bakito/adguardhome-sync/pkg/client/model" "github.com/bakito/adguardhome-sync/pkg/client/model"
"github.com/bakito/adguardhome-sync/pkg/log" "github.com/bakito/adguardhome-sync/pkg/log"
@@ -14,20 +16,18 @@ import (
"github.com/bakito/adguardhome-sync/pkg/utils" "github.com/bakito/adguardhome-sync/pkg/utils"
"github.com/bakito/adguardhome-sync/pkg/versions" "github.com/bakito/adguardhome-sync/pkg/versions"
"github.com/bakito/adguardhome-sync/version" "github.com/bakito/adguardhome-sync/version"
"github.com/robfig/cron/v3"
"go.uber.org/zap"
) )
var l = log.GetLogger("sync") var l = log.GetLogger("sync")
// Sync config from origin to replica // Sync config from origin to replica.
func Sync(cfg *types.Config) error { func Sync(cfg *types.Config) error {
if cfg.Origin.URL == "" { if cfg.Origin.URL == "" {
return fmt.Errorf("origin URL is required") return errors.New("origin URL is required")
} }
if len(cfg.UniqueReplicas()) == 0 { if len(cfg.UniqueReplicas()) == 0 {
return fmt.Errorf("no replicas configured") return errors.New("no replicas configured")
} }
l.With( l.With(
@@ -41,10 +41,8 @@ func Sync(cfg *types.Config) error {
cfg.Origin.AutoSetup = false cfg.Origin.AutoSetup = false
w := &worker{ w := &worker{
cfg: cfg, cfg: cfg,
createClient: func(ai types.AdGuardInstance) (client.Client, error) { createClient: client.New,
return client.New(ai)
},
} }
if cfg.Cron != "" { if cfg.Cron != "" {
w.cron = cron.New() w.cron = cron.New()
@@ -120,15 +118,15 @@ func (w *worker) status() *syncStatus {
return syncStatus return syncStatus
} }
func (w *worker) getStatus(inst types.AdGuardInstance) (st replicaStatus) { func (w *worker) getStatus(inst types.AdGuardInstance) replicaStatus {
st = replicaStatus{Host: inst.WebHost, URL: inst.WebURL} st := replicaStatus{Host: inst.WebHost, URL: inst.WebURL}
oc, err := w.createClient(inst) oc, err := w.createClient(inst)
if err != nil { if err != nil {
l.With("error", err, "url", w.cfg.Origin.URL).Error("Error creating origin client") l.With("error", err, "url", w.cfg.Origin.URL).Error("Error creating origin client")
st.Status = "danger" st.Status = "danger"
st.Error = err.Error() st.Error = err.Error()
return return st
} }
sl := l.With("from", inst.WebHost) sl := l.With("from", inst.WebHost)
status, err := oc.Status() status, err := oc.Status()
@@ -136,16 +134,16 @@ func (w *worker) getStatus(inst types.AdGuardInstance) (st replicaStatus) {
if errors.Is(err, client.ErrSetupNeeded) { if errors.Is(err, client.ErrSetupNeeded) {
st.Status = "warning" st.Status = "warning"
st.Error = err.Error() st.Error = err.Error()
return return st
} }
sl.With("error", err).Error("Error getting origin status") sl.With("error", err).Error("Error getting origin status")
st.Status = "danger" st.Status = "danger"
st.Error = err.Error() st.Error = err.Error()
return return st
} }
st.Status = "success" st.Status = "success"
st.ProtectionEnabled = utils.Ptr(status.ProtectionEnabled) st.ProtectionEnabled = utils.Ptr(status.ProtectionEnabled)
return return st
} }
func (w *worker) sync() { func (w *worker) sync() {

View File

@@ -3,16 +3,17 @@ package sync
import ( import (
"errors" "errors"
"github.com/google/uuid"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
gm "go.uber.org/mock/gomock"
"github.com/bakito/adguardhome-sync/pkg/client" "github.com/bakito/adguardhome-sync/pkg/client"
"github.com/bakito/adguardhome-sync/pkg/client/model" "github.com/bakito/adguardhome-sync/pkg/client/model"
clientmock "github.com/bakito/adguardhome-sync/pkg/mocks/client" clientmock "github.com/bakito/adguardhome-sync/pkg/mocks/client"
"github.com/bakito/adguardhome-sync/pkg/types" "github.com/bakito/adguardhome-sync/pkg/types"
"github.com/bakito/adguardhome-sync/pkg/utils" "github.com/bakito/adguardhome-sync/pkg/utils"
"github.com/bakito/adguardhome-sync/pkg/versions" "github.com/bakito/adguardhome-sync/pkg/versions"
"github.com/google/uuid"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
gm "go.uber.org/mock/gomock"
) )
var _ = Describe("Sync", func() { var _ = Describe("Sync", func() {

View File

@@ -25,7 +25,7 @@ func NewFeatures(enabled bool) Features {
} }
} }
// Features feature flags // Features feature flags.
type Features struct { type Features struct {
DNS DNS `json:"dns" yaml:"dns"` DNS DNS `json:"dns" yaml:"dns"`
DHCP DHCP `json:"dhcp" yaml:"dhcp"` DHCP DHCP `json:"dhcp" yaml:"dhcp"`
@@ -38,20 +38,20 @@ type Features struct {
Theme bool `json:"theme" yaml:"theme" env:"FEATURES_THEME"` Theme bool `json:"theme" yaml:"theme" env:"FEATURES_THEME"`
} }
// DHCP features // DHCP features.
type DHCP struct { type DHCP struct {
ServerConfig bool `json:"serverConfig" yaml:"serverConfig" env:"FEATURES_DHCP_SERVER_CONFIG"` ServerConfig bool `env:"FEATURES_DHCP_SERVER_CONFIG" json:"serverConfig" yaml:"serverConfig"`
StaticLeases bool `json:"staticLeases" yaml:"staticLeases" env:"FEATURES_DHCP_STATIC_LEASES"` StaticLeases bool `env:"FEATURES_DHCP_STATIC_LEASES" json:"staticLeases" yaml:"staticLeases"`
} }
// DNS features // DNS features.
type DNS struct { type DNS struct {
AccessLists bool `json:"accessLists" yaml:"accessLists" env:"FEATURES_DNS_ACCESS_LISTS"` AccessLists bool `env:"FEATURES_DNS_ACCESS_LISTS" json:"accessLists" yaml:"accessLists"`
ServerConfig bool `json:"serverConfig" yaml:"serverConfig" env:"FEATURES_DNS_SERVER_CONFIG"` ServerConfig bool `env:"FEATURES_DNS_SERVER_CONFIG" json:"serverConfig" yaml:"serverConfig"`
Rewrites bool `json:"rewrites" yaml:"rewrites" env:"FEATURES_DNS_REWRITES"` Rewrites bool `env:"FEATURES_DNS_REWRITES" json:"rewrites" yaml:"rewrites"`
} }
// LogDisabled log all disabled features // LogDisabled log all disabled features.
func (f *Features) LogDisabled(l *zap.SugaredLogger) { func (f *Features) LogDisabled(l *zap.SugaredLogger) {
features := f.collectDisabled() features := f.collectDisabled()

View File

@@ -11,72 +11,72 @@ import (
) )
const ( const (
// DefaultAPIPath default api path // DefaultAPIPath default api path.
DefaultAPIPath = "/control" DefaultAPIPath = "/control"
) )
// Config application configuration struct // Config application configuration struct
// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen=true
type Config struct { type Config struct {
Origin AdGuardInstance `json:"origin" yaml:"origin" env:"ORIGIN"` Origin AdGuardInstance `env:"ORIGIN" json:"origin" yaml:"origin"`
Replica *AdGuardInstance `json:"replica,omitempty" yaml:"replica,omitempty" env:"REPLICA"` Replica *AdGuardInstance `env:"REPLICA" json:"replica,omitempty" yaml:"replica,omitempty"`
Replicas []AdGuardInstance `json:"replicas,omitempty" yaml:"replicas,omitempty" faker:"slice_len=2"` Replicas []AdGuardInstance ` json:"replicas,omitempty" yaml:"replicas,omitempty" faker:"slice_len=2"`
Cron string `json:"cron,omitempty" yaml:"cron,omitempty" env:"CRON"` Cron string `env:"CRON" json:"cron,omitempty" yaml:"cron,omitempty"`
RunOnStart bool `json:"runOnStart,omitempty" yaml:"runOnStart,omitempty" env:"RUN_ON_START"` RunOnStart bool `env:"RUN_ON_START" json:"runOnStart,omitempty" yaml:"runOnStart,omitempty"`
PrintConfigOnly bool `json:"printConfigOnly,omitempty" yaml:"printConfigOnly,omitempty" env:"PRINT_CONFIG_ONLY"` PrintConfigOnly bool `env:"PRINT_CONFIG_ONLY" json:"printConfigOnly,omitempty" yaml:"printConfigOnly,omitempty"`
ContinueOnError bool `json:"continueOnError,omitempty" yaml:"continueOnError,omitempty" env:"CONTINUE_ON_ERROR"` ContinueOnError bool `env:"CONTINUE_ON_ERROR" json:"continueOnError,omitempty" yaml:"continueOnError,omitempty"`
API API `json:"api,omitempty" yaml:"api,omitempty" env:"API"` API API `env:"API" json:"api,omitempty" yaml:"api,omitempty"`
Features Features `json:"features,omitempty" yaml:"features,omitempty" env:"FEATURES_"` Features Features `env:"FEATURES_" json:"features,omitempty" yaml:"features,omitempty"`
} }
// API configuration // API configuration.
type API struct { type API struct {
Port int `json:"port,omitempty" yaml:"port,omitempty" env:"API_PORT"` Port int `env:"API_PORT" json:"port,omitempty" yaml:"port,omitempty"`
Username string `json:"username,omitempty" yaml:"username,omitempty" env:"API_USERNAME"` Username string `env:"API_USERNAME" json:"username,omitempty" yaml:"username,omitempty"`
Password string `json:"password,omitempty" yaml:"password,omitempty" env:"API_PASSWORD"` Password string `env:"API_PASSWORD" json:"password,omitempty" yaml:"password,omitempty"`
DarkMode bool `json:"darkMode,omitempty" yaml:"darkMode,omitempty" env:"API_DARK_MODE"` DarkMode bool `env:"API_DARK_MODE" json:"darkMode,omitempty" yaml:"darkMode,omitempty"`
Metrics Metrics `json:"metrics,omitempty" yaml:"metrics,omitempty" env:"API_METRICS"` Metrics Metrics `env:"API_METRICS" json:"metrics,omitempty" yaml:"metrics,omitempty"`
TLS TLS `json:"tls,omitempty" yaml:"tls,omitempty" env:"API_TLS"` TLS TLS `env:"API_TLS" json:"tls,omitempty" yaml:"tls,omitempty"`
} }
// Metrics configuration // Metrics configuration.
type Metrics struct { type Metrics struct {
Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty" env:"API_METRICS_ENABLED"` Enabled bool `env:"API_METRICS_ENABLED" json:"enabled,omitempty" yaml:"enabled,omitempty"`
ScrapeInterval time.Duration `json:"scrapeInterval,omitempty" yaml:"scrapeInterval,omitempty" env:"API_METRICS_SCRAPE_INTERVAL"` ScrapeInterval time.Duration `env:"API_METRICS_SCRAPE_INTERVAL" json:"scrapeInterval,omitempty" yaml:"scrapeInterval,omitempty"`
QueryLogLimit int `json:"queryLogLimit,omitempty" yaml:"queryLogLimit,omitempty" env:"API_METRICS_QUERY_LOG_LIMIT"` QueryLogLimit int `env:"API_METRICS_QUERY_LOG_LIMIT" json:"queryLogLimit,omitempty" yaml:"queryLogLimit,omitempty"`
} }
// TLS configuration // TLS configuration.
type TLS struct { type TLS struct {
CertDir string `json:"certDir,omitempty" yaml:"certDir,omitempty" env:"API_TLS_CERT_DIR"` CertDir string `env:"API_TLS_CERT_DIR" json:"certDir,omitempty" yaml:"certDir,omitempty"`
CertName string `json:"certName,omitempty" yaml:"certName,omitempty" env:"API_TLS_CERT_NAME"` CertName string `env:"API_TLS_CERT_NAME" json:"certName,omitempty" yaml:"certName,omitempty"`
KeyName string `json:"keyName,omitempty" yaml:"keyName,omitempty" env:"API_TLS_KEY_NAME"` KeyName string `env:"API_TLS_KEY_NAME" json:"keyName,omitempty" yaml:"keyName,omitempty"`
} }
func (t TLS) Enabled() bool { func (t TLS) Enabled() bool {
return strings.TrimSpace(t.CertDir) != "" return strings.TrimSpace(t.CertDir) != ""
} }
func (t TLS) Certs() (cert string, key string) { func (t TLS) Certs() (cert, key string) {
cert = filepath.Join(t.CertDir, defaultIfEmpty(t.CertName, "tls.crt")) cert = filepath.Join(t.CertDir, defaultIfEmpty(t.CertName, "tls.crt"))
key = filepath.Join(t.CertDir, defaultIfEmpty(t.KeyName, "tls.key")) key = filepath.Join(t.CertDir, defaultIfEmpty(t.KeyName, "tls.key"))
return return cert, key
} }
func defaultIfEmpty(val string, fallback string) string { func defaultIfEmpty(val, fallback string) string {
if strings.TrimSpace(val) == "" { if strings.TrimSpace(val) == "" {
return fallback return fallback
} }
return val return val
} }
// Mask maks username and password // Mask maks username and password.
func (a *API) Mask() { func (a *API) Mask() {
a.Username = mask(a.Username) a.Username = mask(a.Username)
a.Password = mask(a.Password) a.Password = mask(a.Password)
} }
// UniqueReplicas get unique replication instances // 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 && cfg.Replica.URL != "" { if cfg.Replica != nil && cfg.Replica.URL != "" {
@@ -101,7 +101,7 @@ func (cfg *Config) UniqueReplicas() []AdGuardInstance {
return r return r
} }
// Log the current config // Log the current config.
func (cfg *Config) Log(l *zap.SugaredLogger) { func (cfg *Config) Log(l *zap.SugaredLogger) {
c := cfg.mask() c := cfg.mask()
l.With("config", c).Debug("Using config") l.With("config", c).Debug("Using config")
@@ -140,27 +140,27 @@ func (cfg *Config) Init() error {
// AdGuardInstance AdguardHome config instance // AdGuardInstance AdguardHome config instance
// +k8s:deepcopy-gen=true // +k8s:deepcopy-gen=true
type AdGuardInstance struct { type AdGuardInstance struct {
URL string `json:"url" yaml:"url" env:"URL" faker:"url"` URL string `env:"URL" faker:"url" json:"url" yaml:"url"`
WebURL string `json:"webURL" yaml:"webURL" env:"WEB_URL" faker:"url"` WebURL string `env:"WEB_URL" faker:"url" json:"webURL" yaml:"webURL"`
APIPath string `json:"apiPath,omitempty" yaml:"apiPath,omitempty" env:"API_PATH"` APIPath string `env:"API_PATH" json:"apiPath,omitempty" yaml:"apiPath,omitempty"`
Username string `json:"username,omitempty" yaml:"username,omitempty" env:"USERNAME"` Username string `env:"USERNAME" json:"username,omitempty" yaml:"username,omitempty"`
Password string `json:"password,omitempty" yaml:"password,omitempty" env:"PASSWORD"` Password string `env:"PASSWORD" json:"password,omitempty" yaml:"password,omitempty"`
Cookie string `json:"cookie,omitempty" yaml:"cookie,omitempty" env:"COOKIE"` Cookie string `env:"COOKIE" json:"cookie,omitempty" yaml:"cookie,omitempty"`
InsecureSkipVerify bool `json:"insecureSkipVerify" yaml:"insecureSkipVerify" env:"INSECURE_SKIP_VERIFY"` InsecureSkipVerify bool `env:"INSECURE_SKIP_VERIFY" json:"insecureSkipVerify" yaml:"insecureSkipVerify"`
AutoSetup bool `json:"autoSetup" yaml:"autoSetup" env:"AUTO_SETUP"` AutoSetup bool `env:"AUTO_SETUP" json:"autoSetup" yaml:"autoSetup"`
InterfaceName string `json:"interfaceName,omitempty" yaml:"interfaceName,omitempty" env:"INTERFACE_NAME"` InterfaceName string `env:"INTERFACE_NAME" json:"interfaceName,omitempty" yaml:"interfaceName,omitempty"`
DHCPServerEnabled *bool `json:"dhcpServerEnabled,omitempty" yaml:"dhcpServerEnabled,omitempty" env:"DHCP_SERVER_ENABLED"` DHCPServerEnabled *bool `env:"DHCP_SERVER_ENABLED" json:"dhcpServerEnabled,omitempty" yaml:"dhcpServerEnabled,omitempty"`
Host string `json:"-" yaml:"-"` Host string `json:"-" yaml:"-"`
WebHost string `json:"-" yaml:"-"` WebHost string `json:"-" yaml:"-"`
} }
// Key AdGuardInstance key // 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)
} }
// Mask maks username and password // Mask maks username and password.
func (i *AdGuardInstance) Mask() { func (i *AdGuardInstance) Mask() {
i.Username = mask(i.Username) i.Username = mask(i.Username)
i.Password = mask(i.Password) i.Password = mask(i.Password)
@@ -194,12 +194,12 @@ func mask(s string) string {
return fmt.Sprintf("%v%s%v", string(s[0]), mask, string(s[len(s)-1])) return fmt.Sprintf("%v%s%v", string(s[0]), mask, string(s[len(s)-1]))
} }
// Protection API struct // Protection API struct.
type Protection struct { type Protection struct {
ProtectionEnabled bool `json:"protection_enabled"` ProtectionEnabled bool `json:"protection_enabled"`
} }
// InstallConfig AdguardHome install config // InstallConfig AdguardHome install config.
type InstallConfig struct { type InstallConfig struct {
Web InstallPort `json:"web"` Web InstallPort `json:"web"`
DNS InstallPort `json:"dns"` DNS InstallPort `json:"dns"`
@@ -207,7 +207,7 @@ type InstallConfig struct {
Password string `json:"password"` Password string `json:"password"`
} }
// InstallPort AdguardHome install config port // InstallPort AdguardHome install config port.
type InstallPort struct { type InstallPort struct {
IP string `json:"ip"` IP string `json:"ip"`
Port int `json:"port"` Port int `json:"port"`

View File

@@ -1,15 +1,18 @@
package utils package utils
import "encoding/json" import (
"bytes"
"encoding/json"
)
func Clone[I interface{}](in I, out I) I { func Clone[I any](in, out I) I {
b, _ := json.Marshal(in) b, _ := json.Marshal(in)
_ = json.Unmarshal(b, out) _ = json.Unmarshal(b, out)
return out return out
} }
func JsonEquals(a interface{}, b interface{}) bool { func JSONEquals(a, b any) bool {
ja, _ := json.Marshal(a) ja, _ := json.Marshal(a)
jb, _ := json.Marshal(b) jb, _ := json.Marshal(b)
return string(ja) == string(jb) return bytes.Equal(ja, jb)
} }

View File

@@ -2,18 +2,18 @@ package utils
import "fmt" import "fmt"
func Ptr[I interface{}](i I) *I { func Ptr[I any](i I) *I {
return &i return &i
} }
func PtrToString[I interface{}](i *I) string { func PtrToString[I any](i *I) string {
if i == nil { if i == nil {
return "" return ""
} }
return fmt.Sprintf("%v", i) return fmt.Sprintf("%v", i)
} }
func PtrEquals[I comparable](a *I, b *I) bool { func PtrEquals[I comparable](a, b *I) bool {
if a == nil && b == nil { if a == nil && b == nil {
return true return true
} }

View File

@@ -3,15 +3,15 @@ package versions
import "golang.org/x/mod/semver" import "golang.org/x/mod/semver"
const ( const (
// MinAgh minimal adguardhome version // MinAgh minimal adguardhome version.
MinAgh = "v0.107.40" MinAgh = "v0.107.40"
) )
func IsNewerThan(v1 string, v2 string) bool { func IsNewerThan(v1, v2 string) bool {
return semver.Compare(sanitize(v1), sanitize(v2)) == 1 return semver.Compare(sanitize(v1), sanitize(v2)) == 1
} }
func IsSame(v1 string, v2 string) bool { func IsSame(v1, v2 string) bool {
return semver.Compare(sanitize(v1), sanitize(v2)) == 0 return semver.Compare(sanitize(v1), sanitize(v2)) == 0
} }

View File

@@ -1,9 +1,10 @@
package versions_test package versions_test
import ( import (
"github.com/bakito/adguardhome-sync/pkg/versions"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/bakito/adguardhome-sync/pkg/versions"
) )
var _ = Describe("Versions", func() { var _ = Describe("Versions", func() {

View File

@@ -1,8 +1,8 @@
package version package version
var ( var (
// Version the module version // Version the module version.
Version = "devel" Version = "devel"
// Build the build time // Build the build time.
Build = "N/A" Build = "N/A"
) )