diff --git a/.dockerignore b/.dockerignore index 6c73d84..e3bae30 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,7 @@ -.env +.env build docs +e2e_tests mkdocs.yml venv diff --git a/.github/workflows/reusable-unittests.yml b/.github/workflows/reusable-unittests.yml index 98c26da..8abda4b 100644 --- a/.github/workflows/reusable-unittests.yml +++ b/.github/workflows/reusable-unittests.yml @@ -16,7 +16,7 @@ jobs: go-version: "1.25.4" - name: Run tests with coverage - run: go test -coverprofile=coverage.out ./... + run: go test -tags=unit -coverprofile=coverage.out ./... - name: Upload coverage report uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index c53405f..1a4a14b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .env - -build \ No newline at end of file +build +e2e_tests/.env.test +e2e_tests/adguardhome-data/* +e2e_tests/npm-data/* diff --git a/e2e_tests/e2e_test.go b/e2e_tests/e2e_test.go new file mode 100644 index 0000000..f10006b --- /dev/null +++ b/e2e_tests/e2e_test.go @@ -0,0 +1,374 @@ +//go:build e2e + +package e2e_tests + +import ( + "context" + "fmt" + "os" + "sync" + "testing" + "time" + + "github.com/caarlos0/env/v11" + containerApi "github.com/docker/docker/api/types/container" + imageApi "github.com/docker/docker/api/types/image" + dockerApi "github.com/docker/docker/client" + "github.com/docker/go-connections/nat" + "github.com/joho/godotenv" + "github.com/stretchr/testify/assert" + + "github.com/deepspace2/plugnpin/pkg/clients/adguardhome" + "github.com/deepspace2/plugnpin/pkg/clients/docker" + "github.com/deepspace2/plugnpin/pkg/clients/npm" + "github.com/deepspace2/plugnpin/pkg/clients/pihole" + "github.com/deepspace2/plugnpin/pkg/logging" + "github.com/deepspace2/plugnpin/pkg/processor" +) + +type config struct { + AdguardHomeTag string `env:"ADGUARD_HOME_IMAGE_TAG,required,notEmpty"` + NpmTag string `env:"NPM_IMAGE_TAG,required,notEmpty"` + PiholeTag string `env:"PIHOLE_IMAGE_TAG,required,notEmpty"` +} + +const ( + adguardHomeContainerName = "plugnpin-e2e-test-adguardhome" + adguardHomeImage = "adguard/adguardhome" + + npmContainerName = "plugnpin-e2e-test-npm" + npmImage = "jc21/nginx-proxy-manager" + + piholeContainerName = "plugnpin-e2e-test-pihole" + piholeImage = "pihole/pihole" + + testContainerImage = "busybox" + testContainerName = "plugnpin-e2e-test-testcontainer" +) + +var logger = logging.GetLogger() + +func getEnvVars() (*config, error) { + err := godotenv.Load(".env.test") + if err != nil { + return nil, err + } + var config config + if err := env.Parse(&config); err != nil { + return nil, err + } + return &config, nil +} + +func pullRequiredImages(t *testing.T, ctx context.Context, dockerApi *dockerApi.Client, containers []Container) { + var wg sync.WaitGroup + for i := range containers { + wg.Go(func() { + err := pullImage(ctx, dockerApi, containers[i].image) + if err != nil { + t.Fatalf("Failed to pull image %s: %v", containers[i].image, err) + } + }) + } + wg.Wait() +} + +func startRequiredContainers(t *testing.T, ctx context.Context, dockerCli *dockerApi.Client, containers []Container) { + var wg sync.WaitGroup + for i := range containers { + wg.Go(func() { + cfg := &containerApi.Config{ + Cmd: containers[i].cmd, + Env: containers[i].env, + Image: containers[i].image, + Labels: containers[i].labels, + ExposedPorts: make(nat.PortSet), + } + + // Automatically populate ExposedPorts from the PortBindings in HostConfig + if containers[i].hostConfig != nil { + for port := range containers[i].hostConfig.PortBindings { + cfg.ExposedPorts[port] = struct{}{} + } + } + + response, err := dockerCli.ContainerCreate( + ctx, + cfg, + containers[i].hostConfig, + nil, + nil, + containers[i].name, + ) + if err != nil { + t.Fatalf("Failed to create container %s: %v", containers[i].name, err) + } + containers[i].id = response.ID + logger.Info("container started", "name", containers[i].name, "id", containers[i].id) + err = dockerCli.ContainerStart(ctx, containers[i].id, containerApi.StartOptions{}) + if err != nil { + t.Fatalf("Failed to start container %s: %v", containers[i].name, err) + } + + if containers[i].exposedPort != "" { + containerInfo, err := dockerCli.ContainerInspect(ctx, containers[i].id) + if err != nil { + t.Fatalf("Failed to inspect container %s: %v", containers[i].name, err) + } + bindings := containerInfo.NetworkSettings.Ports[containers[i].exposedPort] + if len(bindings) > 0 { + hostPort := bindings[0].HostPort + containers[i].url = fmt.Sprintf("http://127.0.0.1:%s", hostPort) + logger.Info("Discovered URL for container", "name", containers[i].name, "url", containers[i].url) + } + } + }) + } + wg.Wait() +} + +func setClients(t *testing.T, containers []Container) (*docker.Client, *pihole.Client, *npm.Client, *adguardhome.Client) { + dockerClient, err := docker.NewClient() + if err != nil { + t.Fatalf("Failed to create docker client: %v", err) + } + + var piholeURL, npmURL, adguardHomeURL string + for _, c := range containers { + switch c.name { + case piholeContainerName: + piholeURL = c.url + case npmContainerName: + npmURL = c.url + case adguardHomeContainerName: + adguardHomeURL = c.url + } + } + + piholeClient := pihole.NewClient(piholeURL) + logger.Info("Waiting for Pi-hole to be ready...") + piholeLoginTimeout := time.After(60 * time.Second) + piholeLoginTicker := time.NewTicker(3 * time.Second) + defer piholeLoginTicker.Stop() +PiholeLoginLoop: + for { + select { + case <-piholeLoginTimeout: + t.Fatalf("Timed out waiting for Pi-hole to be ready at %s", piholeURL) + case <-piholeLoginTicker.C: + err = piholeClient.Login("password") + if err == nil { + logger.Info("Successfully logged into Pi-hole") + break PiholeLoginLoop + } + logger.Error("Pi-hole not ready, retrying...", "error", err) + } + } + + npmClient := npm.NewClient(npmURL, "a@a.com", "aaaaaaaa") + logger.Info("Waiting for Nginx Proxy Manager to be ready...") + npmLoginTimeout := time.After(60 * time.Second) + npmLoginTicker := time.NewTicker(3 * time.Second) + defer npmLoginTicker.Stop() +NPMLoginLoop: + for { + select { + case <-npmLoginTimeout: + t.Fatalf("Timed out waiting for Nginx Proxy Manager to be ready at %s", npmURL) + case <-npmLoginTicker.C: + err = npmClient.Login() + if err == nil { + logger.Info("Successfully logged into Nginx Proxy Manager") + break NPMLoginLoop + } + logger.Error("NPM not ready, retrying...", "error", err) + } + } + + adguardHomeClient := adguardhome.NewClient(adguardHomeURL, "", "") + + return dockerClient, piholeClient, npmClient, adguardHomeClient +} + +func setup(t *testing.T, ctx context.Context, dockerCli *dockerApi.Client, containers []Container) (*docker.Client, *pihole.Client, *npm.Client, *adguardhome.Client) { + pullRequiredImages(t, ctx, dockerCli, containers) + startRequiredContainers(t, ctx, dockerCli, containers) + dockerClient, piholeClient, npmClient, adguardHomeClient := setClients(t, containers) + return dockerClient, piholeClient, npmClient, adguardHomeClient +} + +func cleanup(t *testing.T, ctx context.Context, dockerCli *dockerApi.Client, containers []Container, npmClient *npm.Client) { + logger.Info("In cleanup") + + npmProxyHosts, err := npmClient.GetProxyHosts() + if err != nil { + t.Fatalf("Failed to get NPM proxy hosts in cleanup: %v", err) + } + for domain := range npmProxyHosts { + npmClient.DeleteProxyHost(domain) + } + + var wg sync.WaitGroup + for i := range containers { + wg.Go(func() { + err := dockerCli.ContainerRemove(ctx, containers[i].id, containerApi.RemoveOptions{ + Force: true, + }) + if err != nil { + t.Fatalf("Could not remove container %s: %v", containers[i].name, err) + } + + _, err = dockerCli.ImageRemove(ctx, containers[i].image, imageApi.RemoveOptions{ + Force: true, + }) + if err != nil { + t.Fatalf("Could not remove image %s: %v", containers[i].image, err) + } + }) + } + wg.Wait() +} + +func TestE2E(t *testing.T) { + logger.Info("In TestE2E") + + conf, err := getEnvVars() + if err != nil { + t.Fatalf("Failed to load e2e test env vars: %v", err) + } + + ctx := context.Background() + dockerCli, err := dockerApi.NewClientWithOpts(dockerApi.FromEnv) + if err != nil { + t.Fatalf("Failed to create docker api client: %v", err) + } + + workingDir, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get working directory: %v", err) + } + + containers := []Container{ + { + image: fmt.Sprintf("%s:%s", npmImage, conf.NpmTag), + name: npmContainerName, + hostConfig: &containerApi.HostConfig{ + Binds: []string{ + fmt.Sprintf("%s/npm-data/data:/data", workingDir), + fmt.Sprintf("%s/npm-data/letsencrypt:/etc/letsencrypt", workingDir), + }, + PortBindings: nat.PortMap{ + nat.Port("81/tcp"): []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: ""}}, + }, + }, + exposedPort: nat.Port("81/tcp"), + }, + { + env: []string{`FTLCONF_webserver_api_password=password`}, + image: fmt.Sprintf("%s:%s", piholeImage, conf.PiholeTag), + name: piholeContainerName, + hostConfig: &containerApi.HostConfig{ + PortBindings: nat.PortMap{ + nat.Port("80/tcp"): []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: ""}}, + }, + }, + exposedPort: nat.Port("80/tcp"), + }, + { + image: fmt.Sprintf("%s:%s", adguardHomeImage, conf.AdguardHomeTag), + name: adguardHomeContainerName, + hostConfig: &containerApi.HostConfig{ + PortBindings: nat.PortMap{ + nat.Port("3000/tcp"): []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: ""}}, + }, + Binds: []string{ + fmt.Sprintf("%s/adguardhome-data/conf:/opt/adguardhome/conf", workingDir), + }, + }, + exposedPort: nat.Port("3000/tcp"), + }, + { + image: testContainerImage, + cmd: []string{"tail", "-f", "/dev/null"}, + labels: map[string]string{ + docker.IpLabel: "1.1.1.1:8080", + docker.UrlLabel: "busybox.home", + }, + name: testContainerName, + hostConfig: &containerApi.HostConfig{}, + }, + } + + dockerClient, piholeClient, npmClient, adguardHomeClient := setup(t, ctx, dockerCli, containers) + + t.Cleanup(func() { + cleanup(t, ctx, dockerCli, containers, npmClient) + }) + + time.Sleep(2 * time.Second) + + proc := processor.New( + dockerClient, + adguardHomeClient, + piholeClient, + npmClient, + false, + ) + + proc.RunOnce() + + piholeDnsRecords, err := piholeClient.GetDnsRecords() + if err != nil { + t.Fatalf("Failed to get pihole DNS records: %v", err) + } + + npmProxyHosts, err := npmClient.GetProxyHosts() + if err != nil { + t.Fatalf("Failed to get NPM proxy hosts: %v", err) + } + + adguardDnsRewrites, err := adguardHomeClient.GetDnsRewrites() + if err != nil { + t.Fatalf("Failed to get AdGuard Home DNS rewrites: %v", err) + } + + for _, container := range containers { + url, dockerUrlLabelExists := container.labels[docker.UrlLabel] + if dockerUrlLabelExists { + piholeDnsRecordIP, exists := piholeDnsRecords[pihole.DomainName(url)] + + // Assert that the "add" functionality worked + assert.True(t, exists, "A pihole DNS record should exist for the url %s", url) + assert.Equal(t, pihole.IP(npmClient.GetIP()), piholeDnsRecordIP, "The pihole DNS record should point to the NPM container's IP") + assert.Contains(t, npmProxyHosts, url, "The NPM proxy hosts should contain the url %s", url) + + adguardHomeDnsRewriteIP, exists := adguardDnsRewrites[adguardhome.DomainName(url)] + assert.True(t, exists, "An AdGuard Home DNS rewrite should exist for the url %s", url) + assert.Equal(t, adguardhome.IP(npmClient.GetIP()), adguardHomeDnsRewriteIP, "The AdGuard Home DNS rewrite should point to the NPM container's IP") + + // Deleting from pihole and npm so we can assert delete functionality + piholeClient.DeleteDnsRecord(url) + npmClient.DeleteProxyHost(url) + adguardHomeClient.DeleteDnsRewrite(url, npmClient.GetIP()) + + piholeDnsRecords, err := piholeClient.GetDnsRecords() + if err != nil { + t.Fatalf("Failed to get pihole DNS records after delete: %v", err) + } + npmProxyHosts, err := npmClient.GetProxyHosts() + if err != nil { + t.Fatalf("Failed to get NPM proxy hosts after delete: %v", err) + } + adguardDnsRewrites, err := adguardHomeClient.GetDnsRewrites() + if err != nil { + t.Fatalf("Failed to get AdGuard Home DNS rewrites after delete: %v", err) + } + + // Assert that the "delete" functionality worked + assert.NotContains(t, piholeDnsRecords, pihole.DomainName(url), "The pihole DNS record should be deleted for %s", url) + assert.NotContains(t, npmProxyHosts, url, "The NPM proxy host should be deleted for %s", url) + assert.NotContains(t, adguardDnsRewrites, url, "The AdGuard Home DNS rewrite should be deleted for %s", url) + + } + } +} diff --git a/e2e_tests/types.go b/e2e_tests/types.go new file mode 100644 index 0000000..f2ff1e3 --- /dev/null +++ b/e2e_tests/types.go @@ -0,0 +1,20 @@ +//go:build e2e + +package e2e_tests + +import ( + containerApi "github.com/docker/docker/api/types/container" + "github.com/docker/go-connections/nat" +) + +type Container struct { + cmd []string + env []string + exposedPort nat.Port + hostConfig *containerApi.HostConfig + id string + image string + labels map[string]string + name string + url string +} diff --git a/e2e_tests/utils.go b/e2e_tests/utils.go new file mode 100644 index 0000000..fb041c2 --- /dev/null +++ b/e2e_tests/utils.go @@ -0,0 +1,26 @@ +//go:build e2e + +package e2e_tests + +import ( + "context" + "os" + + imageApi "github.com/docker/docker/api/types/image" + dockerCliClient "github.com/docker/docker/client" + "github.com/docker/docker/pkg/jsonmessage" + "github.com/moby/term" +) + +func pullImage(ctx context.Context, dockerCli *dockerCliClient.Client, img string) error { + reader, err := dockerCli.ImagePull(ctx, img, imageApi.PullOptions{}) + if err != nil { + return err + } + + defer reader.Close() + termFd, isTerm := term.GetFdInfo(os.Stderr) + jsonmessage.DisplayJSONMessagesStream(reader, os.Stderr, termFd, isTerm, nil) + return nil +} + diff --git a/go.mod b/go.mod index 0ee80a4..6ece847 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,11 @@ go 1.25.4 require ( github.com/caarlos0/env/v11 v11.3.1 - github.com/docker/docker v28.3.2+incompatible + github.com/docker/docker v28.5.2+incompatible github.com/docker/go-connections v0.5.0 github.com/docker/go-sdk/client v0.1.0-alpha009 github.com/joho/godotenv v1.5.1 - github.com/moby/moby/api v1.52.0 + github.com/moby/term v0.5.2 github.com/spf13/pflag v1.0.7 github.com/stretchr/testify v1.10.0 ) @@ -16,39 +16,24 @@ require ( require ( github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect - github.com/containerd/fifo v1.1.0 // indirect - github.com/containerd/log v0.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-sdk/config v0.1.0-alpha009 // indirect github.com/docker/go-sdk/context v0.1.0-alpha009 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/sys/atomicwriter v0.1.0 // indirect github.com/moby/sys/sequential v0.6.0 // indirect - github.com/moby/sys/userns v0.1.0 // indirect - github.com/moby/term v0.5.2 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.1.0 // indirect - github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect - github.com/prometheus/common v0.6.0 // indirect - github.com/prometheus/procfs v0.0.3 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/otel v1.37.0 // indirect @@ -56,7 +41,6 @@ require ( go.opentelemetry.io/otel/trace v1.37.0 // indirect golang.org/x/net v0.37.0 // indirect golang.org/x/sys v0.33.0 // indirect - golang.org/x/time v0.11.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a // indirect google.golang.org/protobuf v1.36.7 // indirect diff --git a/go.sum b/go.sum index 5e820d4..e1c69c8 100644 --- a/go.sum +++ b/go.sum @@ -2,12 +2,6 @@ github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEK github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA= github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= @@ -16,23 +10,18 @@ github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= -github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= -github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/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= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5XymfYn5vsqeA5oA= -github.com/docker/docker v28.3.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= +github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= -github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-sdk/client v0.1.0-alpha009 h1:7IKRNOKChT99s33UuEBYdzOStToMSznxEz3VOpiqobc= github.com/docker/go-sdk/client v0.1.0-alpha009/go.mod h1:nIDGWTv9QeLI+6dPDMIuXi0S2bd8UNks4O5J1ogvSFA= github.com/docker/go-sdk/config v0.1.0-alpha009 h1:3FiWk7qVGvIfz+Ds4smFkIiopjQjs5xuoncYn8xQWVQ= @@ -43,105 +32,49 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 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/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg= -github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= -github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= @@ -162,64 +95,26 @@ go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mx go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-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.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-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= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -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.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= -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= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-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/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ= google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA= google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a h1:tPE/Kp+x9dMSwUm/uM0JKK0IfdiJkwAbSMSeZBXXJXc= google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= diff --git a/pkg/clients/adguardhome/adguardhome.go b/pkg/clients/adguardhome/adguardhome.go index 7515e07..fdde405 100644 --- a/pkg/clients/adguardhome/adguardhome.go +++ b/pkg/clients/adguardhome/adguardhome.go @@ -33,7 +33,7 @@ func NewClient(baseURL, username, password string) *Client { } } -func (ad *Client) getDnsRewrites() (DnsRewrites, error) { +func (ad *Client) GetDnsRewrites() (DnsRewrites, error) { dnsRewritesResponseString, _, err := clients.Get(&ad.Client, ad.baseURL+"/rewrite/list", headers) if err != nil { return nil, err @@ -49,7 +49,7 @@ func (ad *Client) getDnsRewrites() (DnsRewrites, error) { } func (ad *Client) AddDnsRewrite(domain, ip string) error { - existingRecords, err := ad.getDnsRewrites() + existingRecords, err := ad.GetDnsRewrites() if err != nil { return err } diff --git a/pkg/clients/adguardhome/adguardhome_test.go b/pkg/clients/adguardhome/adguardhome_test.go index 545cf6a..5666216 100644 --- a/pkg/clients/adguardhome/adguardhome_test.go +++ b/pkg/clients/adguardhome/adguardhome_test.go @@ -1,3 +1,5 @@ +//go:build unit + package adguardhome import ( @@ -98,7 +100,7 @@ func TestDeleteDnsRewrite(t *testing.T) { assert.NoError(t, err) assert.True(t, deleteCalled, "Delete API endpoint was not called") - existingDnsRewrites, err := client.getDnsRewrites() + existingDnsRewrites, err := client.GetDnsRewrites() assert.NoError(t, err) assert.Equal(t, 0, len(existingDnsRewrites)) }) diff --git a/pkg/clients/docker/docker.go b/pkg/clients/docker/docker.go index 9dac520..966da5d 100644 --- a/pkg/clients/docker/docker.go +++ b/pkg/clients/docker/docker.go @@ -27,8 +27,8 @@ type ClientOptions struct { var log = logging.GetLogger() const ( - ipLabel = "plugNPiN.ip" - urlLabel = "plugNPiN.url" + IpLabel = "plugNPiN.ip" + UrlLabel = "plugNPiN.url" adguardHomeOptionsTargetDomainLabel = "plugNPiN.adguardHomeOptions.targetDomain" npmOptionsAdvancedConfigLabel = "plugNPiN.npmOptions.advancedConfig" @@ -44,7 +44,7 @@ const ( piholeOptionsTargetDomainLabel = "plugNPiN.piholeOptions.targetDomain" ) -var labels []string = []string{ipLabel, urlLabel} +var labels []string = []string{IpLabel, UrlLabel} type Client struct { *dockerSdk.Client @@ -76,24 +76,24 @@ func GetParsedContainerName(container container.Summary) string { } func GetValuesFromLabels(labels map[string]string) (ip, url string, port int, opts *ClientOptions, err error) { - ip, ok := labels[ipLabel] + ip, ok := labels[IpLabel] if !ok { - return "", "", 0, nil, &errors.NonExistingLabelsError{Msg: fmt.Sprintf("missing %s label", ipLabel)} + return "", "", 0, nil, &errors.NonExistingLabelsError{Msg: fmt.Sprintf("missing %s label", IpLabel)} } - url, ok = labels[urlLabel] + url, ok = labels[UrlLabel] if !ok { - return "", "", 0, nil, &errors.NonExistingLabelsError{Msg: fmt.Sprintf("missing %s label", urlLabel)} + return "", "", 0, nil, &errors.NonExistingLabelsError{Msg: fmt.Sprintf("missing %s label", UrlLabel)} } splitIPAndPort := strings.Split(ip, ":") if len(splitIPAndPort) == 1 { - return "", "", 0, nil, &errors.MalformedIPLabelError{Msg: fmt.Sprintf("missing ':' in value of '%v' label", ipLabel)} + return "", "", 0, nil, &errors.MalformedIPLabelError{Msg: fmt.Sprintf("missing ':' in value of '%v' label", IpLabel)} } ip = splitIPAndPort[0] port, err = strconv.Atoi(splitIPAndPort[1]) if err != nil { return "", "", 0, nil, &errors.MalformedIPLabelError{ - Msg: fmt.Sprintf("value after ':' in value of '%v' label must be an integer, got '%v'", ipLabel, splitIPAndPort[1]), + Msg: fmt.Sprintf("value after ':' in value of '%v' label must be an integer, got '%v'", IpLabel, splitIPAndPort[1]), } } diff --git a/pkg/clients/docker/docker_test.go b/pkg/clients/docker/docker_test.go index 9c387e2..dc7f3cd 100644 --- a/pkg/clients/docker/docker_test.go +++ b/pkg/clients/docker/docker_test.go @@ -1,3 +1,5 @@ +//go:build unit + package docker import ( @@ -60,25 +62,25 @@ func TestGetParsedContainerName(t *testing.T) { func TestGetValuesFromContainerLabels(t *testing.T) { testCases := []struct { - name string - container container.Summary - expectedIP string - expectedURL string - expectedPort int - expectedErr error + name string + container container.Summary + expectedIP string + expectedURL string + expectedPort int + expectedErr error expectedAdguardHomeOptionsTargetDomain string - expectedNpmOptionsBlockExploits bool - expectedNpmOptionsCachingEnabled bool - expectedNpmOptionsScheme string - expectedNpmOptionsWebsocketsSupport bool - expectedPiholeOptionsTargetDomain string + expectedNpmOptionsBlockExploits bool + expectedNpmOptionsCachingEnabled bool + expectedNpmOptionsScheme string + expectedNpmOptionsWebsocketsSupport bool + expectedPiholeOptionsTargetDomain string }{ { name: "Happy path", container: container.Summary{ Labels: map[string]string{ - ipLabel: "192.168.1.10:8080", - urlLabel: "my-service.example.com", + IpLabel: "192.168.1.10:8080", + UrlLabel: "my-service.example.com", }, }, expectedIP: "192.168.1.10", @@ -94,34 +96,34 @@ func TestGetValuesFromContainerLabels(t *testing.T) { name: "Malformed IP label - missing port", container: container.Summary{ Labels: map[string]string{ - ipLabel: "192.168.1.10", - urlLabel: "my-service.example.com", + IpLabel: "192.168.1.10", + UrlLabel: "my-service.example.com", }, }, expectedIP: "", expectedURL: "", expectedPort: 0, - expectedErr: &errors.MalformedIPLabelError{Msg: fmt.Sprintf("missing ':' in value of '%v' label", ipLabel)}, + expectedErr: &errors.MalformedIPLabelError{Msg: fmt.Sprintf("missing ':' in value of '%v' label", IpLabel)}, }, { name: "Malformed IP label - non-integer port", container: container.Summary{ Labels: map[string]string{ - ipLabel: "192.168.1.10:http", - urlLabel: "my-service.example.com", + IpLabel: "192.168.1.10:http", + UrlLabel: "my-service.example.com", }, }, expectedIP: "", expectedURL: "", expectedPort: 0, - expectedErr: &errors.MalformedIPLabelError{Msg: fmt.Sprintf("value after ':' in value of '%v' label must be an integer, got 'http'", ipLabel)}, + expectedErr: &errors.MalformedIPLabelError{Msg: fmt.Sprintf("value after ':' in value of '%v' label must be an integer, got 'http'", IpLabel)}, }, { name: "NPM options", container: container.Summary{ Labels: map[string]string{ - ipLabel: "192.168.1.10:8080", - urlLabel: "my-service.example.com", + IpLabel: "192.168.1.10:8080", + UrlLabel: "my-service.example.com", npmOptionsBlockExploitsLabel: "", npmOptionsCachingEnabledLabel: "true", npmOptionsSchemeLabel: "https", @@ -141,8 +143,8 @@ func TestGetValuesFromContainerLabels(t *testing.T) { name: "NPM options - true values", container: container.Summary{ Labels: map[string]string{ - ipLabel: "192.168.1.10:8080", - urlLabel: "my-service.example.com", + IpLabel: "192.168.1.10:8080", + UrlLabel: "my-service.example.com", npmOptionsBlockExploitsLabel: "true", npmOptionsCachingEnabledLabel: "1", npmOptionsWebsocketsSupportLabel: "T", @@ -161,8 +163,8 @@ func TestGetValuesFromContainerLabels(t *testing.T) { name: "NPM options - false values", container: container.Summary{ Labels: map[string]string{ - ipLabel: "192.168.1.10:8080", - urlLabel: "my-service.example.com", + IpLabel: "192.168.1.10:8080", + UrlLabel: "my-service.example.com", npmOptionsBlockExploitsLabel: "false", npmOptionsCachingEnabledLabel: "0", npmOptionsWebsocketsSupportLabel: "F", @@ -181,8 +183,8 @@ func TestGetValuesFromContainerLabels(t *testing.T) { name: "NPM options - invalid boolean values", container: container.Summary{ Labels: map[string]string{ - ipLabel: "192.168.1.10:8080", - urlLabel: "my-service.example.com", + IpLabel: "192.168.1.10:8080", + UrlLabel: "my-service.example.com", npmOptionsBlockExploitsLabel: "yes", npmOptionsCachingEnabledLabel: "no", npmOptionsWebsocketsSupportLabel: "2", @@ -201,8 +203,8 @@ func TestGetValuesFromContainerLabels(t *testing.T) { name: "NPM options - invalid scheme", container: container.Summary{ Labels: map[string]string{ - ipLabel: "192.168.1.10:8080", - urlLabel: "my-service.example.com", + IpLabel: "192.168.1.10:8080", + UrlLabel: "my-service.example.com", npmOptionsSchemeLabel: "invalid", }, }, @@ -215,8 +217,8 @@ func TestGetValuesFromContainerLabels(t *testing.T) { name: "NPM options - case-insensitive scheme", container: container.Summary{ Labels: map[string]string{ - ipLabel: "192.168.1.10:8080", - urlLabel: "my-service.example.com", + IpLabel: "192.168.1.10:8080", + UrlLabel: "my-service.example.com", npmOptionsSchemeLabel: "HTTPS", }, }, @@ -233,8 +235,8 @@ func TestGetValuesFromContainerLabels(t *testing.T) { name: "Pi-Hole options - no target domain", container: container.Summary{ Labels: map[string]string{ - ipLabel: "192.168.1.10:8080", - urlLabel: "my-service.example.com", + IpLabel: "192.168.1.10:8080", + UrlLabel: "my-service.example.com", }, }, expectedIP: "192.168.1.10", @@ -249,8 +251,8 @@ func TestGetValuesFromContainerLabels(t *testing.T) { name: "Pi-Hole options - target domain", container: container.Summary{ Labels: map[string]string{ - ipLabel: "192.168.1.10:8080", - urlLabel: "my-service.example.com", + IpLabel: "192.168.1.10:8080", + UrlLabel: "my-service.example.com", piholeOptionsTargetDomainLabel: "custom.domain", }, }, @@ -266,8 +268,8 @@ func TestGetValuesFromContainerLabels(t *testing.T) { name: "AdguardHome options - target domain", container: container.Summary{ Labels: map[string]string{ - ipLabel: "192.168.1.10:8080", - urlLabel: "my-service.example.com", + IpLabel: "192.168.1.10:8080", + UrlLabel: "my-service.example.com", adguardHomeOptionsTargetDomainLabel: "custom.domain.adguard", }, }, diff --git a/pkg/clients/npm/npm.go b/pkg/clients/npm/npm.go index eea1735..f0865de 100644 --- a/pkg/clients/npm/npm.go +++ b/pkg/clients/npm/npm.go @@ -87,7 +87,7 @@ func (n *Client) GetIP() string { return url.Hostname() } -func (n *Client) getProxyHosts() (map[string]int, error) { +func (n *Client) GetProxyHosts() (map[string]int, error) { proxyHostsString, statusCode, err := n.makeRequest(http.MethodGet, n.baseURL+"/nginx/proxy-hosts", nil) if err != nil || statusCode >= 400 { return nil, err @@ -178,7 +178,7 @@ func (n *Client) GetCertificateIDByName(name string) *int { } func (n *Client) AddProxyHost(host ProxyHost) error { - existingProxyHosts, err := n.getProxyHosts() + existingProxyHosts, err := n.GetProxyHosts() if err != nil { return err } @@ -208,7 +208,7 @@ func (n *Client) AddProxyHost(host ProxyHost) error { } func (n *Client) DeleteProxyHost(domain string) error { - existingProxyHosts, err := n.getProxyHosts() + existingProxyHosts, err := n.GetProxyHosts() if err != nil { return err } diff --git a/pkg/clients/npm/npm_test.go b/pkg/clients/npm/npm_test.go index 11f67fa..34ecb64 100644 --- a/pkg/clients/npm/npm_test.go +++ b/pkg/clients/npm/npm_test.go @@ -1,3 +1,5 @@ +//go:build unit + package npm import ( diff --git a/pkg/clients/pihole/pihole.go b/pkg/clients/pihole/pihole.go index 9b4da9d..177db03 100644 --- a/pkg/clients/pihole/pihole.go +++ b/pkg/clients/pihole/pihole.go @@ -67,7 +67,7 @@ func dnsRecordToRaw(domain DomainName, ip IP) string { return fmt.Sprintf("%v %v", ip, domain) } -func (p *Client) getDnsRecords() (DnsRecords, error) { +func (p *Client) GetDnsRecords() (DnsRecords, error) { if p.sid == "" { log.Error("Missing Pi-Hole session ID") os.Exit(1) @@ -92,7 +92,7 @@ func (p *Client) getDnsRecords() (DnsRecords, error) { } func (p *Client) AddDnsRecord(domain, ip string) error { - existingRecords, err := p.getDnsRecords() + existingRecords, err := p.GetDnsRecords() if err != nil { return err } @@ -141,7 +141,7 @@ func (p *Client) AddDnsRecord(domain, ip string) error { } func (p *Client) DeleteDnsRecord(domain string) error { - existingRecords, err := p.getDnsRecords() + existingRecords, err := p.GetDnsRecords() if err != nil { return err } diff --git a/pkg/clients/pihole/pihole_test.go b/pkg/clients/pihole/pihole_test.go index f733eeb..aa635ec 100644 --- a/pkg/clients/pihole/pihole_test.go +++ b/pkg/clients/pihole/pihole_test.go @@ -1,3 +1,5 @@ +//go:build unit + package pihole import ( diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 7c387a4..dc3b427 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -1,3 +1,5 @@ +//go:build unit + package config import (