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:
go-version-file: "go.mod"
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
skip-cache: true
- name: Lint
run: make lint
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
# renovate: packageName=mvdan.cc/gofumpt
TB_GOFUMPT_VERSION ?= v0.7.0
# renovate: packageName=github.com/golangci/golangci-lint/cmd/golangci-lint
TB_GOLANGCI_LINT_VERSION ?= v1.64.8
# renovate: packageName=github.com/golangci/golangci-lint/v2/cmd/golangci-lint
TB_GOLANGCI_LINT_VERSION ?= v2.0.1
# renovate: packageName=github.com/segmentio/golines
TB_GOLINES_VERSION ?= v0.12.2
# renovate: packageName=github.com/goreleaser/goreleaser/v2
@@ -53,7 +53,7 @@ $(TB_GOFUMPT): $(TB_LOCALBIN)
.PHONY: tb.golangci-lint
tb.golangci-lint: $(TB_GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
$(TB_GOLANGCI_LINT): $(TB_LOCALBIN)
test -s $(TB_LOCALBIN)/golangci-lint || GOBIN=$(TB_LOCALBIN) go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(TB_GOLANGCI_LINT_VERSION)
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
tb.golines: $(TB_GOLINES) ## Download golines locally if necessary.
$(TB_GOLINES): $(TB_LOCALBIN)
@@ -95,7 +95,7 @@ tb.update: tb.reset
toolbox makefile --renovate -f $(TB_LOCALDIR)/Makefile \
k8s.io/code-generator/cmd/deepcopy-gen@github.com/kubernetes/code-generator \
mvdan.cc/gofumpt@github.com/mvdan/gofumpt \
github.com/golangci/golangci-lint/cmd/golangci-lint \
github.com/golangci/golangci-lint/v2/cmd/golangci-lint \
github.com/segmentio/golines \
github.com/goreleaser/goreleaser/v2 \
go.uber.org/mock/mockgen@github.com/uber-go/mock \

View File

@@ -4,9 +4,10 @@ import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/bakito/adguardhome-sync/pkg/log"
"github.com/bakito/adguardhome-sync/version"
"github.com/spf13/cobra"
)
var (
@@ -14,7 +15,7 @@ var (
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{
Use: "adguardhome-sync",
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.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
_, _ = fmt.Println(err)
os.Exit(1)
}
}

View File

@@ -1,16 +1,17 @@
package cmd
import (
"github.com/spf13/cobra"
"github.com/bakito/adguardhome-sync/pkg/config"
"github.com/bakito/adguardhome-sync/pkg/log"
"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{
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`,
RunE: func(cmd *cobra.Command, args []string) error {
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.FlagPrintConfigOnly, false, "Prints the configuration only and exists. "+
"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.")
doCmd.PersistentFlags().
Int(config.FlagApiPort, 8080, "Sync API Port, the API endpoint will be started to enable remote triggering; if 0 port API is disabled.")
doCmd.PersistentFlags().String(config.FlagApiUsername, "", "Sync API username")
doCmd.PersistentFlags().String(config.FlagApiPassword, "", "Sync API password")
doCmd.PersistentFlags().String(config.FlagApiDarkMode, "", "API UI in dark mode")
Int(config.FlagAPIPort, 8080, "Sync API Port, the API endpoint will be started to enable remote triggering; if 0 port API is disabled.")
doCmd.PersistentFlags().String(config.FlagAPIUsername, "", "Sync API username")
doCmd.PersistentFlags().String(config.FlagAPIPassword, "", "Sync API password")
doCmd.PersistentFlags().String(config.FlagAPIDarkMode, "", "API UI in dark mode")
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.FlagFeatureDnsServerConfig, true, "Enable DNS server config 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.FlagFeatureDNSServerConfig, true, "Enable DNS server config 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.FlagFeatureGeneral, true, "Enable general settings 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.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.FlagOriginPassword, "", "Origin instance password")
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.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.FlagReplicaPassword, "", "Replica instance password")
doCmd.PersistentFlags().String(config.FlagReplicaCookie, "", "If Set, uses a cookie for authentication")

View File

@@ -1,6 +1,7 @@
package main
import (
"context"
"fmt"
"io"
"log"
@@ -20,21 +21,33 @@ func main() {
}
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),
http.NoBody,
)
if err != nil {
log.Fatal(err)
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Fatalln(err)
}
defer func() { _ = resp.Body.Close() }()
data, err := io.ReadAll(resp.Body)
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)
if err != nil {
log.Fatalln(err)
log.Println(err)
return
}
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 {
if allOf, ok, _ := unstructured.NestedSlice(dnsInfo, "allOf"); ok && len(allOf) == 2 {
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 {
log.Fatalln(err)
log.Println(err)
return
}
}
}
@@ -60,24 +75,26 @@ func main() {
b, err := yaml.Marshal(&schema)
if err != nil {
log.Fatalln(err)
log.Println(err)
return
}
log.Printf("Writing schema file tmp/%s", fileName)
err = os.WriteFile("tmp/"+fileName, b, 0o600)
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
if err := unstructured.SetNestedField(schema, "string", "components", "schemas", "QueryLogItem", "properties", "client_proto", "type"); err != nil {
log.Fatalln(err)
}
}
func addFakeTags(schema map[string]interface{}) {
fake := map[string]interface{}{"faker": `slice_len=24`}
func addFakeTags(schema map[string]any) {
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 {
log.Fatalln(err)
}

View File

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

View File

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

View File

@@ -8,13 +8,14 @@ import (
"os"
"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/model"
"github.com/bakito/adguardhome-sync/pkg/types"
"github.com/bakito/adguardhome-sync/pkg/utils"
"github.com/google/uuid"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var (
@@ -140,7 +141,7 @@ var _ = Describe("Client", func() {
ts, cl = ClientPost(
"/install/configure",
fmt.Sprintf(
`{"web":{"ip":"0.0.0.0","port":3000,"status":"","can_autofix":false},"dns":{"ip":"0.0.0.0","port":53,"status":"","can_autofix":false},"username":"%s","password":"%s"}`,
`{"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,
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) {
Ω(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())
w.Header().Set("Content-Type", "application/json")
_, err = w.Write(b)

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,12 +4,13 @@ import (
"encoding/json"
"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/onsi/ginkgo/v2"
. "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() {

View File

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

View File

@@ -3,11 +3,12 @@ package config_test
import (
"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/gomega"
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() {
@@ -16,7 +17,7 @@ var _ = Describe("Config", func() {
flags *flagsmock.MockFlags
mockCtrl *gm.Controller
changedEnvVars []string
setEnv = func(name string, value string) {
setEnv = func(name, value string) {
_ = os.Setenv(name, value)
changedEnvVars = append(changedEnvVars, name)
}
@@ -125,9 +126,9 @@ var _ = Describe("Config", func() {
Ω(cfg.Get().API.Port).Should(Equal(9090))
})
It("should have the api port from the config flags", func() {
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes()
flags.EXPECT().Changed(config.FlagAPIPort).Return(true).AnyTimes()
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
flags.EXPECT().GetInt(config.FlagApiPort).Return(9990, nil).AnyTimes()
flags.EXPECT().GetInt(config.FlagAPIPort).Return(9990, nil).AnyTimes()
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
Ω(err).ShouldNot(HaveOccurred())
@@ -135,9 +136,9 @@ var _ = Describe("Config", func() {
})
It("should have the api port from the config env var", func() {
setEnv("API_PORT", "9999")
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes()
flags.EXPECT().Changed(config.FlagAPIPort).Return(true).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)
Ω(err).ShouldNot(HaveOccurred())
@@ -174,9 +175,9 @@ var _ = Describe("Config", func() {
Ω(cfg.Get().API.Port).Should(Equal(9090))
})
It("should have the api port from the config flags", func() {
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes()
flags.EXPECT().Changed(config.FlagAPIPort).Return(true).AnyTimes()
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
flags.EXPECT().GetInt(config.FlagApiPort).Return(9990, nil).AnyTimes()
flags.EXPECT().GetInt(config.FlagAPIPort).Return(9990, nil).AnyTimes()
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
Ω(err).ShouldNot(HaveOccurred())
@@ -184,9 +185,9 @@ var _ = Describe("Config", func() {
})
It("should have the api port from the config env var", func() {
setEnv("API_PORT", "9999")
flags.EXPECT().Changed(config.FlagApiPort).Return(true).AnyTimes()
flags.EXPECT().Changed(config.FlagAPIPort).Return(true).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)
Ω(err).ShouldNot(HaveOccurred())
@@ -202,9 +203,9 @@ var _ = Describe("Config", func() {
Ω(cfg.Get().Features.DNS.ServerConfig).Should(BeFalse())
})
It("should have the feature dns server config from the config flags", func() {
flags.EXPECT().Changed(config.FlagFeatureDnsServerConfig).Return(true).AnyTimes()
flags.EXPECT().Changed(config.FlagFeatureDNSServerConfig).Return(true).AnyTimes()
flags.EXPECT().Changed(gm.Any()).Return(false).AnyTimes()
flags.EXPECT().GetBool(config.FlagFeatureDnsServerConfig).Return(true, nil).AnyTimes()
flags.EXPECT().GetBool(config.FlagFeatureDNSServerConfig).Return(true, nil).AnyTimes()
cfg, err := config.Get("../../testdata/config_test_replicas.yaml", flags)
Ω(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() {
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().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)
Ω(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() {
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().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)
Ω(err).ShouldNot(HaveOccurred())

View File

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

View File

@@ -6,9 +6,10 @@ import (
"strconv"
"strings"
"github.com/caarlos0/env/v11"
"github.com/bakito/adguardhome-sync/pkg/types"
"github.com/bakito/adguardhome-sync/pkg/utils"
"github.com/caarlos0/env/v11"
)
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)
if oldOK {
logger.With("deprecated", oldName, "replacement", newName).
Warn("Deprecated env variable is used, please use the correct one")
}
new, newOK := os.LookupEnv(newName)
newVal, newOK := os.LookupEnv(newName)
if newOK {
return new, true
return newVal, true
}
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))
}

View File

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

View File

@@ -10,9 +10,6 @@ import (
)
var _ = Describe("Config", func() {
var ()
BeforeEach(func() {
})
Context("configFilePath", func() {
It("should return the same value", func() {
path := uuid.NewString()
@@ -26,7 +23,7 @@ var _ = Describe("Config", func() {
result, err := configFilePath("")
Ω(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"
FlagContinueOnError = "continueOnError"
FlagApiPort = "api-port"
FlagApiUsername = "api-username"
FlagApiPassword = "api-password"
FlagApiDarkMode = "api-dark-mode"
FlagAPIPort = "api-port"
FlagAPIUsername = "api-username"
FlagAPIPassword = "api-password"
FlagAPIDarkMode = "api-dark-mode"
FlagFeatureDhcpServerConfig = "feature-dhcp-server-config"
FlagFeatureDhcpStaticLeases = "feature-dhcp-static-leases"
FlagFeatureDnsServerConfig = "feature-dns-server-config"
FlagFeatureDnsAccessLists = "feature-dns-access-lists"
FlagFeatureDnsRewrites = "feature-dns-rewrites"
FlagFeatureDNSServerConfig = "feature-dns-server-config"
FlagFeatureDNSAccessLists = "feature-dns-access-lists"
FlagFeatureDNSRewrites = "feature-dns-rewrites"
FlagFeatureGeneral = "feature-general-settings"
FlagFeatureQueryLog = "feature-query-log-config"
FlagFeatureStats = "feature-stats-config"
@@ -25,7 +25,7 @@ const (
FlagOriginURL = "origin-url"
FlagOriginWebURL = "origin-web-url"
FlagOriginApiPath = "origin-api-path"
FlagOriginAPIPath = "origin-api-path"
FlagOriginUsername = "origin-username"
FlagOriginPassword = "origin-password"
@@ -34,7 +34,7 @@ const (
FlagReplicaURL = "replica-url"
FlagReplicaWebURL = "replica-web-url"
FlagReplicaApiPath = "replica-api-path"
FlagReplicaAPIPath = "replica-api-path"
FlagReplicaUsername = "replica-username"
FlagReplicaPassword = "replica-password"
FlagReplicaCookie = "replica-cookie"

View File

@@ -18,7 +18,7 @@ func readFlags(cfg *types.Config, flags Flags) error {
return err
}
if err := fr.readApiFlags(); err != nil {
if err := fr.readAPIFlags(); err != nil {
return err
}
@@ -30,11 +30,7 @@ func readFlags(cfg *types.Config, flags Flags) error {
return err
}
if err := fr.readReplicaFlags(); err != nil {
return err
}
return nil
return fr.readReplicaFlags()
}
type flagReader struct {
@@ -53,7 +49,7 @@ func (fr *flagReader) readReplicaFlags() error {
}); err != nil {
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
}); err != nil {
return err
@@ -83,12 +79,9 @@ func (fr *flagReader) readReplicaFlags() error {
}); err != nil {
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
}); err != nil {
return err
}
return nil
})
}
func (fr *flagReader) readOriginFlags() error {
@@ -102,7 +95,7 @@ func (fr *flagReader) readOriginFlags() error {
}); err != nil {
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
}); err != nil {
return err
@@ -122,12 +115,9 @@ func (fr *flagReader) readOriginFlags() error {
}); err != nil {
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
}); err != nil {
return err
}
return nil
})
}
func (fr *flagReader) readFeatureFlags() error {
@@ -142,17 +132,17 @@ func (fr *flagReader) readFeatureFlags() error {
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
}); err != nil {
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
}); err != nil {
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
}); err != nil {
return err
@@ -183,61 +173,51 @@ func (fr *flagReader) readFeatureFlags() error {
}); err != nil {
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
})
}
func (fr *flagReader) readAPIFlags() error {
if err := fr.setIntFlag(FlagAPIPort, func(cgf *types.Config, value int) {
fr.cfg.API.Port = value
}); err != nil {
return err
}
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) {
if err := fr.setStringFlag(FlagAPIUsername, func(cgf *types.Config, value string) {
fr.cfg.API.Username = value
}); 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
}); 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
}); err != nil {
return
}
return
})
}
func (fr *flagReader) readRootFlags() (err error) {
if err = fr.setStringFlag(FlagCron, func(cgf *types.Config, value string) {
func (fr *flagReader) readRootFlags() error {
if err := fr.setStringFlag(FlagCron, func(cgf *types.Config, value string) {
fr.cfg.Cron = value
}); 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
}); 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
}); 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
}); err != nil {
return
}
return
})
}
type Flags interface {
@@ -249,33 +229,33 @@ type Flags interface {
func (fr *flagReader) setStringFlag(name string, cb callback[string]) (err error) {
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
} else {
cb(fr.cfg, value)
}
cb(fr.cfg, value)
}
return nil
}
func (fr *flagReader) setBoolFlag(name string, cb callback[bool]) (err error) {
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
} else {
cb(fr.cfg, value)
}
cb(fr.cfg, value)
}
return nil
}
func (fr *flagReader) setIntFlag(name string, cb callback[int]) (err error) {
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
} else {
cb(fr.cfg, value)
}
cb(fr.cfg, value)
}
return nil
}

View File

@@ -3,11 +3,12 @@ package config
import (
"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/gomega"
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() {
@@ -88,7 +89,7 @@ var _ = Describe("Config", func() {
}))
})
})
Context("readApiFlags", func() {
Context("readAPIFlags", func() {
It("should change all values", func() {
cfg.API = types.API{
Port: 1111,
@@ -99,10 +100,10 @@ var _ = Describe("Config", func() {
flags.EXPECT().Changed(gm.Any()).DoAndReturn(func(name string) bool {
return strings.HasPrefix(name, "api")
}).AnyTimes()
flags.EXPECT().GetInt(FlagApiPort).Return(9999, nil)
flags.EXPECT().GetString(FlagApiUsername).Return("aaaa", nil)
flags.EXPECT().GetString(FlagApiPassword).Return("bbbb", nil)
flags.EXPECT().GetBool(FlagApiDarkMode).Return(true, nil)
flags.EXPECT().GetInt(FlagAPIPort).Return(9999, nil)
flags.EXPECT().GetString(FlagAPIUsername).Return("aaaa", nil)
flags.EXPECT().GetString(FlagAPIPassword).Return("bbbb", nil)
flags.EXPECT().GetBool(FlagAPIDarkMode).Return(true, nil)
err := readFlags(cfg, flags)
Ω(err).ShouldNot(HaveOccurred())
@@ -154,7 +155,7 @@ var _ = Describe("Config", func() {
flags.EXPECT().Changed(FlagOriginURL).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(FlagOriginPassword).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(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(FlagOriginPassword).Return("e", 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(FlagReplicaWebURL).Return(true)
flags.EXPECT().Changed(FlagReplicaApiPath).Return(true)
flags.EXPECT().Changed(FlagReplicaAPIPath).Return(true)
flags.EXPECT().Changed(FlagReplicaUsername).Return(true)
flags.EXPECT().Changed(FlagReplicaPassword).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(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(FlagReplicaPassword).Return("e", nil)
flags.EXPECT().GetString(FlagReplicaCookie).Return("f", nil)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,12 @@
package metrics
import (
"github.com/bakito/adguardhome-sync/pkg/client/model"
"github.com/go-faker/faker/v4"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/utils/ptr"
"github.com/bakito/adguardhome-sync/pkg/client/model"
)
var _ = Describe("Metrics", func() {
@@ -85,15 +86,15 @@ var _ = Describe("Metrics", func() {
})
})
func verifyStats(lines []line) {
var total line
func verifyStats(lines []Line) {
var total Line
sum := make([]int, len(lines[0].Data))
for _, l := range lines {
if l.Title == labelTotal {
total = l
} else {
for i, d := range l.Data {
sum[i] = sum[i] + d
sum[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()
t := s.Total()
dns := graphLines(t, s, blue, blueAlternatives, func(s *model.Stats) []int {
t = s.Total()
dns = graphLines(t, s, blue, blueAlternatives, func(s *model.Stats) []int {
return safeStats(s.DnsQueries)
})
blocked := graphLines(t, s, red, redAlternatives, func(s *model.Stats) []int {
blocked = graphLines(t, s, red, redAlternatives, func(s *model.Stats) []int {
return safeStats(s.BlockedFiltering)
})
malware := graphLines(t, s, green, greenAlternatives, func(s *model.Stats) []int {
malware = graphLines(t, s, green, greenAlternatives, func(s *model.Stats) []int {
return safeStats(s.ReplacedSafebrowsing)
})
adult := graphLines(t, s, yellow, yellowAlternatives, func(s *model.Stats) []int {
adult = graphLines(t, s, yellow, yellowAlternatives, func(s *model.Stats) []int {
return safeStats(s.ReplacedParental)
})
@@ -79,9 +79,9 @@ func graphLines(
baseColor []int,
altColors [][]int,
dataCB func(s *model.Stats) []int,
) []line {
) []Line {
g := &graph{
total: line{
total: Line{
Fill: true,
Title: labelTotal,
Data: dataCB(t),
@@ -94,7 +94,7 @@ func graphLines(
var i int
for name, data := range s {
if name != StatsTotal {
g.replicas = append(g.replicas, line{
g.replicas = append(g.replicas, Line{
Fill: false,
Title: name,
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)
})
lines = append(lines, g.replicas...)
@@ -116,11 +116,11 @@ func graphLines(
}
type graph struct {
total line
replicas []line
total Line
replicas []Line
}
type line struct {
type Line struct {
Data []int `json:"data"`
R int `json:"r"`
G int `json:"g"`

View File

@@ -384,17 +384,17 @@ func (mr *MockClientMockRecorder) SafeSearchConfig() *gomock.Call {
}
// SetAccessList mocks base method.
func (m *MockClient) SetAccessList(arg0 *model.AccessList) error {
func (m *MockClient) SetAccessList(accessList *model.AccessList) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetAccessList", arg0)
ret := m.ctrl.Call(m, "SetAccessList", accessList)
ret0, _ := ret[0].(error)
return ret0
}
// 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()
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.
@@ -426,31 +426,31 @@ func (mr *MockClientMockRecorder) SetCustomRules(rules any) *gomock.Call {
}
// SetDNSConfig mocks base method.
func (m *MockClient) SetDNSConfig(arg0 *model.DNSConfig) error {
func (m *MockClient) SetDNSConfig(config *model.DNSConfig) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetDNSConfig", arg0)
ret := m.ctrl.Call(m, "SetDNSConfig", config)
ret0, _ := ret[0].(error)
return ret0
}
// 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()
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.
func (m *MockClient) SetDhcpConfig(arg0 *model.DhcpStatus) error {
func (m *MockClient) SetDhcpConfig(status *model.DhcpStatus) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetDhcpConfig", arg0)
ret := m.ctrl.Call(m, "SetDhcpConfig", status)
ret0, _ := ret[0].(error)
return ret0
}
// 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()
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.
@@ -468,17 +468,17 @@ func (mr *MockClientMockRecorder) SetProfileInfo(settings any) *gomock.Call {
}
// SetQueryLogConfig mocks base method.
func (m *MockClient) SetQueryLogConfig(arg0 *model.QueryLogConfigWithIgnored) error {
func (m *MockClient) SetQueryLogConfig(ql *model.QueryLogConfigWithIgnored) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SetQueryLogConfig", arg0)
ret := m.ctrl.Call(m, "SetQueryLogConfig", ql)
ret0, _ := ret[0].(error)
return ret0
}
// 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()
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.

View File

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

View File

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

View File

@@ -14,10 +14,11 @@ import (
"syscall"
"time"
"github.com/gin-gonic/gin"
"github.com/bakito/adguardhome-sync/pkg/log"
"github.com/bakito/adguardhome-sync/pkg/metrics"
"github.com/bakito/adguardhome-sync/version"
"github.com/gin-gonic/gin"
)
var (
@@ -35,13 +36,13 @@ func (w *worker) handleSync(c *gin.Context) {
func (w *worker) handleRoot(c *gin.Context) {
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,
"Metrics": w.cfg.API.Metrics.Enabled,
"Version": version.Version,
"Build": version.Build,
"SyncStatus": w.status(),
"Stats": map[string]interface{}{
"Stats": map[string]any{
"Labels": getLast24Hours(),
"DNS": dns,
"Blocked": blocked,
@@ -169,8 +170,6 @@ func (w *worker) listenAndServe() {
// manually cancel context if not using httpServer.RegisterOnShutdown(cancel)
cancel()
defer os.Exit(0)
}
type syncStatus struct {
@@ -192,7 +191,7 @@ func getLast24Hours() []string {
currentTime := time.Now()
// 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
timeInstance := currentTime.Add(time.Duration(-i) * time.Hour)
timeInstance = timeInstance.Truncate(time.Hour)

View File

@@ -35,16 +35,17 @@ func (w *worker) scrape() {
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)
if err != nil {
l.With("error", err, "url", w.cfg.Origin.URL).Error("Error creating origin client")
return
return im
}
im.HostName = inst.Host
im.Status, _ = client.Status()
im.Stats, _ = client.Stats()
im.QueryLog, _ = client.QueryLog(w.cfg.API.Metrics.QueryLogLimit)
return
return im
}

View File

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

View File

@@ -3,16 +3,17 @@ package sync
import (
"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/model"
clientmock "github.com/bakito/adguardhome-sync/pkg/mocks/client"
"github.com/bakito/adguardhome-sync/pkg/types"
"github.com/bakito/adguardhome-sync/pkg/utils"
"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() {

View File

@@ -25,7 +25,7 @@ func NewFeatures(enabled bool) Features {
}
}
// Features feature flags
// Features feature flags.
type Features struct {
DNS DNS `json:"dns" yaml:"dns"`
DHCP DHCP `json:"dhcp" yaml:"dhcp"`
@@ -38,20 +38,20 @@ type Features struct {
Theme bool `json:"theme" yaml:"theme" env:"FEATURES_THEME"`
}
// DHCP features
// DHCP features.
type DHCP struct {
ServerConfig bool `json:"serverConfig" yaml:"serverConfig" env:"FEATURES_DHCP_SERVER_CONFIG"`
StaticLeases bool `json:"staticLeases" yaml:"staticLeases" env:"FEATURES_DHCP_STATIC_LEASES"`
ServerConfig bool `env:"FEATURES_DHCP_SERVER_CONFIG" json:"serverConfig" yaml:"serverConfig"`
StaticLeases bool `env:"FEATURES_DHCP_STATIC_LEASES" json:"staticLeases" yaml:"staticLeases"`
}
// DNS features
// DNS features.
type DNS struct {
AccessLists bool `json:"accessLists" yaml:"accessLists" env:"FEATURES_DNS_ACCESS_LISTS"`
ServerConfig bool `json:"serverConfig" yaml:"serverConfig" env:"FEATURES_DNS_SERVER_CONFIG"`
Rewrites bool `json:"rewrites" yaml:"rewrites" env:"FEATURES_DNS_REWRITES"`
AccessLists bool `env:"FEATURES_DNS_ACCESS_LISTS" json:"accessLists" yaml:"accessLists"`
ServerConfig bool `env:"FEATURES_DNS_SERVER_CONFIG" json:"serverConfig" yaml:"serverConfig"`
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) {
features := f.collectDisabled()

View File

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

View File

@@ -1,15 +1,18 @@
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)
_ = json.Unmarshal(b, out)
return out
}
func JsonEquals(a interface{}, b interface{}) bool {
func JSONEquals(a, b any) bool {
ja, _ := json.Marshal(a)
jb, _ := json.Marshal(b)
return string(ja) == string(jb)
return bytes.Equal(ja, jb)
}

View File

@@ -2,18 +2,18 @@ package utils
import "fmt"
func Ptr[I interface{}](i I) *I {
func Ptr[I any](i I) *I {
return &i
}
func PtrToString[I interface{}](i *I) string {
func PtrToString[I any](i *I) string {
if i == nil {
return ""
}
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 {
return true
}

View File

@@ -3,15 +3,15 @@ package versions
import "golang.org/x/mod/semver"
const (
// MinAgh minimal adguardhome version
// MinAgh minimal adguardhome version.
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
}
func IsSame(v1 string, v2 string) bool {
func IsSame(v1, v2 string) bool {
return semver.Compare(sanitize(v1), sanitize(v2)) == 0
}

View File

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

View File

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