mirror of
https://github.com/lovelaze/nebula-sync.git
synced 2025-11-05 18:29:19 +01:00
First e2e test implementation using go-testcontainers
This commit is contained in:
committed by
lovelaze
parent
86a2733595
commit
066cc77ced
+7
-4
@@ -15,11 +15,14 @@ var runCmd = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
readEnvFile()
|
||||
|
||||
conf := config.Config{}
|
||||
conf.Load()
|
||||
service, err := service.Init()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to initialize service")
|
||||
}
|
||||
|
||||
service := service.NewService(conf)
|
||||
service.Run()
|
||||
if err = service.Run(); err != nil {
|
||||
log.Fatal().Err(err).Msg("service error")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PiHoleContainer struct {
|
||||
Container testcontainers.Container
|
||||
password string
|
||||
}
|
||||
|
||||
func (c *PiHoleContainer) ConnectionString() string {
|
||||
mappedPort, err := c.Container.MappedPort(context.Background(), "80/tcp")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
hostIP, err := c.Container.Host(context.Background())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("http://%s:%s", hostIP, mappedPort.Port())
|
||||
}
|
||||
|
||||
func (c *PiHoleContainer) EnvString() string {
|
||||
return fmt.Sprintf("%s|%s", c.ConnectionString(), c.password)
|
||||
}
|
||||
|
||||
func RunPiHole(ctx context.Context, password string, opts ...testcontainers.ContainerCustomizer) *PiHoleContainer {
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: "pihole/pihole:development-v6",
|
||||
ExposedPorts: []string{"80/tcp"},
|
||||
WaitingFor: wait.ForListeningPort("80").WithStartupTimeout(10 * time.Second),
|
||||
Env: map[string]string{
|
||||
"FTLCONF_webserver_api_password": password,
|
||||
},
|
||||
}
|
||||
|
||||
genericContainerReq := testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: req,
|
||||
Started: true,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
if err := opt.Customize(&genericContainerReq); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
container, err := testcontainers.GenericContainer(ctx, genericContainerReq)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &PiHoleContainer{Container: container, password: password}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/lovelaze/nebula-sync/internal/service"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testSuite struct {
|
||||
suite.Suite
|
||||
piHole1 *PiHoleContainer
|
||||
piHole2 *PiHoleContainer
|
||||
}
|
||||
|
||||
func (suite *testSuite) SetupTest() {
|
||||
ctx := context.Background()
|
||||
suite.piHole1 = RunPiHole(ctx, "foo1")
|
||||
suite.piHole2 = RunPiHole(ctx, "foo2")
|
||||
}
|
||||
|
||||
func TestE2E(t *testing.T) {
|
||||
suite.Run(t, new(testSuite))
|
||||
}
|
||||
|
||||
func (suite *testSuite) Test_FullSync() {
|
||||
suite.T().Setenv("PRIMARY", suite.piHole1.EnvString())
|
||||
suite.T().Setenv("REPLICAS", suite.piHole2.EnvString())
|
||||
suite.T().Setenv("FULL_SYNC", "true")
|
||||
|
||||
srv, err := service.Init()
|
||||
require.NoError(suite.T(), err)
|
||||
err = srv.Run()
|
||||
require.NoError(suite.T(), err)
|
||||
}
|
||||
|
||||
func (suite *testSuite) Test_ManualSync() {
|
||||
suite.T().Setenv("PRIMARY", suite.piHole1.EnvString())
|
||||
suite.T().Setenv("REPLICAS", suite.piHole2.EnvString())
|
||||
suite.T().Setenv("FULL_SYNC", "false")
|
||||
|
||||
srv, err := service.Init()
|
||||
require.NoError(suite.T(), err)
|
||||
err = srv.Run()
|
||||
require.NoError(suite.T(), err)
|
||||
}
|
||||
@@ -5,7 +5,6 @@ go 1.22
|
||||
require (
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
@@ -23,6 +22,7 @@ require (
|
||||
github.com/containerd/errdefs v0.1.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.1 // indirect
|
||||
github.com/creack/pty v1.1.21 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/docker v27.1.1+incompatible // indirect
|
||||
@@ -37,7 +37,7 @@ require (
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
@@ -50,8 +50,10 @@ require (
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
@@ -62,11 +64,15 @@ require (
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/time v0.1.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||
google.golang.org/grpc v1.59.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
|
||||
@@ -21,8 +21,8 @@ github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoY
|
||||
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -66,8 +66,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
@@ -96,6 +96,7 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@@ -104,8 +105,9 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
@@ -147,14 +149,14 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0/go.mod h1:/OpE/y70qVkndM0TrxT4KBoN3RsFZP0QaofcfYrj76I=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
@@ -170,8 +172,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -196,8 +198,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
|
||||
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
@@ -206,9 +208,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a h1:fwgW9j3vHirt4ObdHoYNwuO24BEZjSzbh+zPaNWoiY8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb h1:lK0oleSc7IQsUxO3U5TjL9DWlsxpEBemh+zpB7IqhWI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
|
||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||
|
||||
@@ -43,14 +43,15 @@ type SyncSettings struct {
|
||||
Config *ManualConfig `ignored:"true"`
|
||||
}
|
||||
|
||||
func (c *Config) Load() {
|
||||
func (c *Config) Load() error {
|
||||
if err := envconfig.Process("", c); err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to process env vars")
|
||||
return err
|
||||
}
|
||||
|
||||
if !c.FullSync {
|
||||
c.loadSyncSettings()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) loadSyncSettings() {
|
||||
|
||||
@@ -15,7 +15,8 @@ func TestConfig_Load(t *testing.T) {
|
||||
t.Setenv("FULL_SYNC", "true")
|
||||
t.Setenv("CRON", "* * * * *")
|
||||
|
||||
conf.Load()
|
||||
err := conf.Load()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "http://localhost:1337", conf.Primary.Url.String())
|
||||
assert.Equal(t, "asdf", conf.Primary.Password)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/lovelaze/nebula-sync/internal/pihole/model"
|
||||
"github.com/lovelaze/nebula-sync/version"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
@@ -20,7 +21,11 @@ var (
|
||||
)
|
||||
|
||||
func NewClient(piHole model.PiHole) Client {
|
||||
return &client{PiHole: piHole}
|
||||
logger := log.With().Str("client", piHole.Url.String()).Logger()
|
||||
return &client{
|
||||
PiHole: piHole,
|
||||
logger: &logger,
|
||||
}
|
||||
}
|
||||
|
||||
type Client interface {
|
||||
@@ -38,6 +43,7 @@ type Client interface {
|
||||
type client struct {
|
||||
PiHole model.PiHole
|
||||
auth auth
|
||||
logger *zerolog.Logger
|
||||
}
|
||||
|
||||
type auth struct {
|
||||
@@ -64,7 +70,7 @@ func (a *auth) verify() error {
|
||||
}
|
||||
|
||||
func (client *client) Authenticate() error {
|
||||
log.Debug().Msgf("Authenticate, client %s", client.String())
|
||||
client.logger.Debug().Msg("Authenticate")
|
||||
authResponse := model.AuthResponse{}
|
||||
|
||||
reqBytes, err := json.Marshal(model.AuthRequest{Password: client.PiHole.Password})
|
||||
@@ -110,7 +116,7 @@ func (client *client) Authenticate() error {
|
||||
}
|
||||
|
||||
func (client *client) DeleteSession() error {
|
||||
log.Debug().Msgf("Delete session, client %s", client.String())
|
||||
client.logger.Debug().Msg("Delete session")
|
||||
if err := client.auth.verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -137,7 +143,7 @@ func (client *client) DeleteSession() error {
|
||||
}
|
||||
|
||||
func (client *client) GetVersion() (*model.VersionResponse, error) {
|
||||
log.Debug().Msgf("Get version, client %s", client.String())
|
||||
client.logger.Debug().Msg("Get version")
|
||||
versionResponse := model.VersionResponse{}
|
||||
if err := client.auth.verify(); err != nil {
|
||||
return &versionResponse, err
|
||||
@@ -170,7 +176,7 @@ func (client *client) GetVersion() (*model.VersionResponse, error) {
|
||||
}
|
||||
|
||||
func (client *client) GetTeleporter() ([]byte, error) {
|
||||
log.Debug().Msgf("Get teleporter, client %s", client.String())
|
||||
client.logger.Debug().Msg("Get teleporter")
|
||||
if err := client.auth.verify(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -195,7 +201,8 @@ func (client *client) GetTeleporter() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (client *client) PostTeleporter(payload []byte, teleporterRequest *model.PostTeleporterRequest) error {
|
||||
log.Debug().Msgf("Post teleporter, client %s, request %v", client.String(), teleporterRequest)
|
||||
client.logger.Debug().Any("payload", teleporterRequest).Msg("Post teleporter")
|
||||
|
||||
if err := client.auth.verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -243,7 +250,7 @@ func (client *client) PostTeleporter(payload []byte, teleporterRequest *model.Po
|
||||
}
|
||||
|
||||
func (client *client) GetConfig() (configResponse *model.ConfigResponse, err error) {
|
||||
log.Debug().Msgf("Get config, client %s", client.String())
|
||||
client.logger.Debug().Msg("Get config")
|
||||
if err := client.auth.verify(); err != nil {
|
||||
return configResponse, err
|
||||
}
|
||||
@@ -277,7 +284,7 @@ func (client *client) GetConfig() (configResponse *model.ConfigResponse, err err
|
||||
}
|
||||
|
||||
func (client *client) PatchConfig(patchRequest *model.PatchConfigRequest) error {
|
||||
log.Debug().Msgf("Patch config, client %s", client.String())
|
||||
client.logger.Debug().Any("payload", patchRequest).Msgf("Patch config")
|
||||
if err := client.auth.verify(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,69 +3,66 @@ package pihole
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/lovelaze/nebula-sync/e2e"
|
||||
"github.com/lovelaze/nebula-sync/internal/pihole/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
"log"
|
||||
tc "github.com/testcontainers/testcontainers-go"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
dockerImage string = "pihole/pihole:development-v6"
|
||||
apiPassword string = "test"
|
||||
)
|
||||
|
||||
var (
|
||||
container = startContainer()
|
||||
piHole = e2e.RunPiHole(context.Background(), apiPassword).Container
|
||||
)
|
||||
|
||||
type ClientTestSuite struct {
|
||||
type clientTestSuite struct {
|
||||
suite.Suite
|
||||
client Client
|
||||
}
|
||||
|
||||
func (suite *ClientTestSuite) SetupTest() {
|
||||
client := createClient(container)
|
||||
func (suite *clientTestSuite) SetupTest() {
|
||||
client := createClient(piHole)
|
||||
err := client.Authenticate()
|
||||
require.NoError(suite.T(), err)
|
||||
suite.client = client
|
||||
}
|
||||
|
||||
func TestClientIntegration(t *testing.T) {
|
||||
suite.Run(t, new(ClientTestSuite))
|
||||
suite.Run(t, new(clientTestSuite))
|
||||
}
|
||||
|
||||
func (suite *ClientTestSuite) TestClient_Authenticate() {
|
||||
func (suite *clientTestSuite) TestClient_Authenticate() {
|
||||
err := suite.client.Authenticate()
|
||||
|
||||
assert.NoError(suite.T(), err)
|
||||
}
|
||||
|
||||
func (suite *ClientTestSuite) TestClient_DeleteSession() {
|
||||
func (suite *clientTestSuite) TestClient_DeleteSession() {
|
||||
err := suite.client.DeleteSession()
|
||||
|
||||
assert.NoError(suite.T(), err)
|
||||
}
|
||||
|
||||
func (suite *ClientTestSuite) TestClient_GetVersion() {
|
||||
func (suite *clientTestSuite) TestClient_GetVersion() {
|
||||
version, err := suite.client.GetVersion()
|
||||
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.NotNil(suite.T(), version)
|
||||
}
|
||||
|
||||
func (suite *ClientTestSuite) TestClient_GetTeleporter() {
|
||||
func (suite *clientTestSuite) TestClient_GetTeleporter() {
|
||||
payload, err := suite.client.GetTeleporter()
|
||||
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.NotNil(suite.T(), payload)
|
||||
}
|
||||
|
||||
func (suite *ClientTestSuite) TestClient_PostTeleporter() {
|
||||
func (suite *clientTestSuite) TestClient_PostTeleporter() {
|
||||
payload, _ := suite.client.GetTeleporter()
|
||||
err := suite.client.PostTeleporter(payload, &model.PostTeleporterRequest{
|
||||
Config: true,
|
||||
@@ -84,14 +81,14 @@ func (suite *ClientTestSuite) TestClient_PostTeleporter() {
|
||||
assert.NoError(suite.T(), err)
|
||||
}
|
||||
|
||||
func (suite *ClientTestSuite) TestClient_GetConfig() {
|
||||
func (suite *clientTestSuite) TestClient_GetConfig() {
|
||||
conf, err := suite.client.GetConfig()
|
||||
|
||||
assert.NoError(suite.T(), err)
|
||||
assert.NotNil(suite.T(), conf)
|
||||
}
|
||||
|
||||
func (suite *ClientTestSuite) TestClient_PatchConfig() {
|
||||
func (suite *clientTestSuite) TestClient_PatchConfig() {
|
||||
request := model.PatchConfigRequest{
|
||||
Config: model.PatchConfig{
|
||||
DNS: nil,
|
||||
@@ -145,29 +142,7 @@ func Test_auth_verify(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func startContainer() testcontainers.Container {
|
||||
containerRequest := testcontainers.ContainerRequest{
|
||||
Image: dockerImage,
|
||||
ExposedPorts: []string{"80/tcp", "53/tcp", "53/udp"},
|
||||
WaitingFor: wait.ForListeningPort("80").WithStartupTimeout(30 * time.Second),
|
||||
Env: map[string]string{
|
||||
"FTLCONF_dns_upstreams": "8.8.8.8",
|
||||
"FTLCONF_webserver_api_password": apiPassword,
|
||||
},
|
||||
}
|
||||
|
||||
container, err := testcontainers.GenericContainer(context.Background(), testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: containerRequest,
|
||||
Started: true,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("starting pihole test container: %v", err)
|
||||
}
|
||||
return container
|
||||
}
|
||||
|
||||
func createClient(container testcontainers.Container) Client {
|
||||
func createClient(container tc.Container) Client {
|
||||
apiPort, err := container.MappedPort(context.Background(), "80/tcp")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -175,5 +150,5 @@ func createClient(container testcontainers.Container) Client {
|
||||
|
||||
host := fmt.Sprintf("http://localhost:%s", apiPort.Port())
|
||||
|
||||
return NewClient(model.NewPiHole(host, "test"))
|
||||
return NewClient(model.NewPiHole(host, apiPassword))
|
||||
}
|
||||
|
||||
@@ -20,5 +20,4 @@ func TestPiHole_Decode(t *testing.T) {
|
||||
|
||||
assert.Equal(t, expectedUrl, ph.Url)
|
||||
assert.Equal(t, pw, ph.Password)
|
||||
|
||||
}
|
||||
|
||||
+24
-12
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lovelaze/nebula-sync/internal/config"
|
||||
"github.com/lovelaze/nebula-sync/internal/pihole"
|
||||
"github.com/lovelaze/nebula-sync/internal/sync"
|
||||
@@ -14,7 +15,12 @@ type Service struct {
|
||||
conf config.Config
|
||||
}
|
||||
|
||||
func NewService(conf config.Config) *Service {
|
||||
func Init() (*Service, error) {
|
||||
conf := config.Config{}
|
||||
if err := conf.Load(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
primary := pihole.NewClient(conf.Primary)
|
||||
var rs []pihole.Client
|
||||
for _, replica := range conf.Replicas {
|
||||
@@ -24,24 +30,25 @@ func NewService(conf config.Config) *Service {
|
||||
return &Service{
|
||||
target: sync.NewTarget(primary, rs),
|
||||
conf: conf,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Run() {
|
||||
func (service *Service) Run() error {
|
||||
log.Info().Msgf("Starting nebula-sync v%s", version.Version)
|
||||
log.Debug().Msgf("Settings cron=%v, fullsync=%v, syncsettings=%v", service.conf.Cron, service.conf.FullSync, service.conf.SyncSettings)
|
||||
|
||||
if service.conf.Cron == nil {
|
||||
service.doSync(service.target)
|
||||
return service.doSync(service.target)
|
||||
} else {
|
||||
service.startCron(func() {
|
||||
service.doSync(service.target)
|
||||
return service.startCron(func() {
|
||||
if err := service.doSync(service.target); err != nil {
|
||||
log.Error().Err(err).Msg("sync failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (service *Service) doSync(t sync.Target) {
|
||||
var err error
|
||||
func (service *Service) doSync(t sync.Target) (err error) {
|
||||
if service.conf.FullSync {
|
||||
err = t.FullSync()
|
||||
} else {
|
||||
@@ -49,19 +56,24 @@ func (service *Service) doSync(t sync.Target) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Sync failed")
|
||||
return
|
||||
return fmt.Errorf("sync failed: %w", err)
|
||||
}
|
||||
|
||||
log.Info().Msg("Sync complete")
|
||||
return err
|
||||
}
|
||||
|
||||
func (service *Service) startCron(cmd func()) {
|
||||
func (service *Service) startCron(cmd func()) error {
|
||||
cron := cron.New()
|
||||
|
||||
if _, err := cron.AddFunc(*service.conf.Cron, cmd); err != nil {
|
||||
log.Fatal().Err(err).Msgf("Failed to start cron: %s", *service.conf.Cron)
|
||||
return fmt.Errorf("failed to start cron job: %w", err)
|
||||
}
|
||||
|
||||
cron.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) Target() sync.Target {
|
||||
return service.target
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lovelaze/nebula-sync/internal/config"
|
||||
"github.com/lovelaze/nebula-sync/internal/pihole"
|
||||
"github.com/lovelaze/nebula-sync/internal/pihole/model"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
@@ -28,15 +28,15 @@ func NewTarget(primary pihole.Client, replicas []pihole.Client) Target {
|
||||
func (target *target) FullSync() error {
|
||||
log.Info().Int("replicas", len(target.Replicas)).Msg("Running full sync")
|
||||
if err := target.authenticate(); err != nil {
|
||||
return errors.Wrap(err, "authentication failed")
|
||||
return fmt.Errorf("authentication failed: %w", err)
|
||||
}
|
||||
|
||||
if err := target.syncTeleporters(nil); err != nil {
|
||||
return errors.Wrap(err, "sync Teleporters failed")
|
||||
return fmt.Errorf("sync Teleporters failed: %w", err)
|
||||
}
|
||||
|
||||
if err := target.deleteSessions(); err != nil {
|
||||
return errors.Wrap(err, "delete sessions failed")
|
||||
return fmt.Errorf("delete sessions failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -46,19 +46,19 @@ func (target *target) ManualSync(syncSettings *config.SyncSettings) error {
|
||||
log.Info().Int("replicas", len(target.Replicas)).Msg("Running manual sync")
|
||||
|
||||
if err := target.authenticate(); err != nil {
|
||||
return errors.Wrap(err, "authentication failed")
|
||||
return fmt.Errorf("authentication failed: %w", err)
|
||||
}
|
||||
|
||||
if err := target.syncTeleporters(syncSettings.Gravity); err != nil {
|
||||
return errors.Wrap(err, "sync Teleporters failed")
|
||||
return fmt.Errorf("sync Teleporters failed: %w", err)
|
||||
}
|
||||
|
||||
if err := target.syncConfigs(syncSettings.Config); err != nil {
|
||||
return errors.Wrap(err, "sync configs failed")
|
||||
return fmt.Errorf("sync configs failed: %w", err)
|
||||
}
|
||||
|
||||
if err := target.deleteSessions(); err != nil {
|
||||
return errors.Wrap(err, "delete sessions failed")
|
||||
return fmt.Errorf("delete sessions failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -116,6 +116,7 @@ func (target *target) syncTeleporters(manualGravity *config.ManualGravity) error
|
||||
}
|
||||
|
||||
func (target *target) syncConfigs(manualConfig *config.ManualConfig) error {
|
||||
log.Info().Msg("Syncing configs...")
|
||||
configResponse, err := target.Primary.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user