diff options
Diffstat (limited to '')
-rw-r--r-- | src/go/plugin/go.d/modules/docker/collect.go | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/src/go/plugin/go.d/modules/docker/collect.go b/src/go/plugin/go.d/modules/docker/collect.go new file mode 100644 index 000000000..f23c58f22 --- /dev/null +++ b/src/go/plugin/go.d/modules/docker/collect.go @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package docker + +import ( + "context" + "fmt" + "strings" + + "github.com/docker/docker/api/types" + typesContainer "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" + typesImage "github.com/docker/docker/api/types/image" +) + +func (d *Docker) collect() (map[string]int64, error) { + if d.client == nil { + client, err := d.newClient(d.Config) + if err != nil { + return nil, err + } + d.client = client + } + + if !d.verNegotiated { + d.verNegotiated = true + d.negotiateAPIVersion() + } + + defer func() { _ = d.client.Close() }() + + mx := make(map[string]int64) + + if err := d.collectInfo(mx); err != nil { + return nil, err + } + if err := d.collectImages(mx); err != nil { + return nil, err + } + if err := d.collectContainers(mx); err != nil { + return nil, err + } + + return mx, nil +} + +func (d *Docker) collectInfo(mx map[string]int64) error { + ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration()) + defer cancel() + + info, err := d.client.Info(ctx) + if err != nil { + return err + } + + mx["containers_state_running"] = int64(info.ContainersRunning) + mx["containers_state_paused"] = int64(info.ContainersPaused) + mx["containers_state_exited"] = int64(info.ContainersStopped) + + return nil +} + +func (d *Docker) collectImages(mx map[string]int64) error { + ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration()) + defer cancel() + + images, err := d.client.ImageList(ctx, typesImage.ListOptions{}) + if err != nil { + return err + } + + mx["images_size"] = 0 + mx["images_dangling"] = 0 + mx["images_active"] = 0 + + for _, v := range images { + mx["images_size"] += v.Size + if v.Containers == 0 { + mx["images_dangling"]++ + } else { + mx["images_active"]++ + } + } + + return nil +} + +var ( + containerHealthStatuses = []string{ + types.Healthy, + types.Unhealthy, + types.Starting, + types.NoHealthcheck, + } + containerStates = []string{ + "created", + "running", + "paused", + "restarting", + "removing", + "exited", + "dead", + } +) + +func (d *Docker) collectContainers(mx map[string]int64) error { + containerSet := make(map[string][]types.Container) + + for _, status := range containerHealthStatuses { + if err := func() error { + ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration()) + defer cancel() + + v, err := d.client.ContainerList(ctx, typesContainer.ListOptions{ + All: true, + Filters: filters.NewArgs(filters.KeyValuePair{Key: "health", Value: status}), + Size: d.CollectContainerSize, + }) + if err != nil { + return err + } + containerSet[status] = v + return nil + + }(); err != nil { + return err + } + } + + seen := make(map[string]bool) + + for _, s := range containerHealthStatuses { + mx["containers_health_status_"+s] = 0 + } + mx["containers_health_status_not_running_unhealthy"] = 0 + + for status, containers := range containerSet { + if status != types.Unhealthy { + mx["containers_health_status_"+status] = int64(len(containers)) + } + + for _, cntr := range containers { + if status == types.Unhealthy { + if cntr.State == "running" { + mx["containers_health_status_"+status] += 1 + } else { + mx["containers_health_status_not_running_unhealthy"] += 1 + } + } + + if len(cntr.Names) == 0 { + continue + } + + name := strings.TrimPrefix(cntr.Names[0], "/") + + seen[name] = true + + if !d.containers[name] { + d.containers[name] = true + d.addContainerCharts(name, cntr.Image) + } + + px := fmt.Sprintf("container_%s_", name) + + for _, s := range containerHealthStatuses { + mx[px+"health_status_"+s] = 0 + } + mx[px+"health_status_not_running_unhealthy"] = 0 + for _, s := range containerStates { + mx[px+"state_"+s] = 0 + } + + if status == types.Unhealthy && cntr.State != "running" { + mx[px+"health_status_not_running_unhealthy"] += 1 + } else { + mx[px+"health_status_"+status] = 1 + } + mx[px+"state_"+cntr.State] = 1 + mx[px+"size_rw"] = cntr.SizeRw + mx[px+"size_root_fs"] = cntr.SizeRootFs + } + } + + for name := range d.containers { + if !seen[name] { + delete(d.containers, name) + d.removeContainerCharts(name) + } + } + + return nil +} + +func (d *Docker) negotiateAPIVersion() { + ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration()) + defer cancel() + + d.client.NegotiateAPIVersion(ctx) +} |