diff --git a/plugins/inputs/dovecot/README.md b/plugins/inputs/dovecot/README.md index ea9f1e160815e..f13ca2afaf110 100644 --- a/plugins/inputs/dovecot/README.md +++ b/plugins/inputs/dovecot/README.md @@ -1,8 +1,14 @@ # Dovecot Input Plugin This plugin uses the Dovecot [v2.1 stats protocol][stats] to gather -metrics on configured domains of [Dovecot][dovecot] servers. You should still -be able to use this protocol on newer versions of Dovecot. +metrics on configured domains of [Dovecot][dovecot] servers. You can use this +plugin on Dovecot up to and including version v2.3.x. + +> [!IMPORTANT] +> Dovecot v2.4+ has the old protocol removed and this plugin will not work. +> Please use Dovecot's [Openmetrics exporter][openmetrics] in combination with +> the [http input plugin][http_plugin] and `openmetrics` data format for newer +> versions of Dovecot. ⭐ Telegraf v0.10.3 🏷️ server @@ -10,6 +16,8 @@ be able to use this protocol on newer versions of Dovecot. [dovecot]: https://www.dovecot.org/ [stats]: https://doc.dovecot.org/configuration_manual/stats/old_statistics/#old-statistics +[http_plugin]: /plugins/inputs/http/README.md +[openmetrics]: https://doc.dovecot.org/latest/core/config/statistics.html#openmetrics ## Global configuration options diff --git a/plugins/inputs/dovecot/dovecot_test.go b/plugins/inputs/dovecot/dovecot_test.go index 19d98f65fa609..818db62ca61c8 100644 --- a/plugins/inputs/dovecot/dovecot_test.go +++ b/plugins/inputs/dovecot/dovecot_test.go @@ -3,7 +3,6 @@ package dovecot import ( "bufio" "bytes" - "fmt" "io" "net" "net/textproto" @@ -195,7 +194,7 @@ func TestDovecotContainerIntegration(t *testing.T) { servicePort := "24242" container := testutil.Container{ - Image: "dovecot/dovecot", + Image: "dovecot/dovecot:2.3-latest", ExposedPorts: []string{servicePort}, Files: map[string]string{ "/etc/dovecot/dovecot.conf": testdata, @@ -205,14 +204,13 @@ func TestDovecotContainerIntegration(t *testing.T) { wait.ForListeningPort(nat.Port(servicePort)), ), } + require.NoError(t, container.Start(), "failed to start container") defer container.Terminate() - err = container.Start() - require.NoError(t, err, "failed to start container") - - d := &Dovecot{Servers: []string{ - fmt.Sprintf("%s:%s", container.Address, container.Ports[servicePort]), - }, Type: "global"} + d := &Dovecot{ + Servers: []string{container.Address + ":" + container.Ports[servicePort]}, + Type: "global", + } var acc testutil.Accumulator require.NoError(t, d.Gather(&acc)) diff --git a/testutil/container.go b/testutil/container.go index 48131d1d26d2f..0a33ed1cb4c3a 100644 --- a/testutil/container.go +++ b/testutil/container.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/image" "github.com/docker/go-connections/nat" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" @@ -80,15 +81,19 @@ func (c *Container) Start() error { c.Logs = TestLogConsumer{} c.container.FollowOutput(&c.Logs) - err = c.container.StartLogProducer(c.ctx) - if err != nil { + if err := c.container.StartLogProducer(c.ctx); err != nil { return fmt.Errorf("log producer failed: %w", err) } c.Address = "localhost" - err = c.LookupMappedPorts() + info, err := c.GetInfo() if err != nil { + return fmt.Errorf("getting info failed: %w", err) + } + fmt.Println("Started container:", info) + + if err := c.LookupMappedPorts(); err != nil { c.Terminate() return fmt.Errorf("port lookup failed: %w", err) } @@ -142,16 +147,13 @@ func (c *Container) PrintLogs() { } func (c *Container) Terminate() { - err := c.container.StopLogProducer() - if err != nil { + if err := c.container.StopLogProducer(); err != nil { fmt.Println(err) } - err = c.container.Terminate(c.ctx) - if err != nil { + if err := c.container.Terminate(c.ctx); err != nil { fmt.Printf("failed to terminate the container: %s", err) } - c.PrintLogs() } @@ -172,3 +174,42 @@ func (c *Container) Resume() error { return provider.Client().ContainerUnpause(c.ctx, c.container.GetContainerID()) } + +func (c *Container) GetInfo() (string, error) { + dc, ok := c.container.(*testcontainers.DockerContainer) + if !ok { + return "not a docker container", nil + } + + ci, err := dc.Inspect(c.ctx) + if err != nil { + return "", fmt.Errorf("inspecting container failed: %w", err) + } + + provider, err := testcontainers.NewDockerProvider() + if err != nil { + return "", fmt.Errorf("getting provider failed: %w", err) + } + + summaries, err := provider.Client().ImageList(c.ctx, image.ListOptions{}) + if err != nil { + return "", fmt.Errorf("listing images failed: %w", err) + } + + for _, s := range summaries { + if s.ID != ci.ContainerJSONBase.Image { + continue + } + var digest []string + for _, d := range s.RepoDigests { + if _, suffix, found := strings.Cut(d, "@"); found { + digest = append(digest, suffix) + } else { + digest = append(digest, d) + } + } + return fmt.Sprintf("%s (%s)", dc.Image, strings.Join(digest, ",")), nil + } + + return "unknown", nil +}