github.com/netdata/go.d.plugin@v0.58.1/modules/docker/collect.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package docker 4 5 import ( 6 "context" 7 "fmt" 8 "strings" 9 10 "github.com/docker/docker/api/types" 11 "github.com/docker/docker/api/types/filters" 12 ) 13 14 func (d *Docker) collect() (map[string]int64, error) { 15 if d.client == nil { 16 client, err := d.newClient(d.Config) 17 if err != nil { 18 return nil, err 19 } 20 d.client = client 21 } 22 23 if !d.verNegotiated { 24 d.verNegotiated = true 25 d.negotiateAPIVersion() 26 } 27 28 defer func() { _ = d.client.Close() }() 29 30 mx := make(map[string]int64) 31 32 if err := d.collectInfo(mx); err != nil { 33 return nil, err 34 } 35 if err := d.collectImages(mx); err != nil { 36 return nil, err 37 } 38 if err := d.collectContainers(mx); err != nil { 39 return nil, err 40 } 41 42 return mx, nil 43 } 44 45 func (d *Docker) collectInfo(mx map[string]int64) error { 46 ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration) 47 defer cancel() 48 49 info, err := d.client.Info(ctx) 50 if err != nil { 51 return err 52 } 53 54 mx["containers_state_running"] = int64(info.ContainersRunning) 55 mx["containers_state_paused"] = int64(info.ContainersPaused) 56 mx["containers_state_exited"] = int64(info.ContainersStopped) 57 58 return nil 59 } 60 61 func (d *Docker) collectImages(mx map[string]int64) error { 62 ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration) 63 defer cancel() 64 65 images, err := d.client.ImageList(ctx, types.ImageListOptions{}) 66 if err != nil { 67 return err 68 } 69 70 mx["images_size"] = 0 71 mx["images_dangling"] = 0 72 mx["images_active"] = 0 73 74 for _, v := range images { 75 mx["images_size"] += v.Size 76 if v.Containers == 0 { 77 mx["images_dangling"]++ 78 } else { 79 mx["images_active"]++ 80 } 81 } 82 83 return nil 84 } 85 86 var ( 87 containerHealthStatuses = []string{ 88 types.Healthy, 89 types.Unhealthy, 90 types.Starting, 91 types.NoHealthcheck, 92 } 93 containerStates = []string{ 94 "created", 95 "running", 96 "paused", 97 "restarting", 98 "removing", 99 "exited", 100 "dead", 101 } 102 ) 103 104 func (d *Docker) collectContainers(mx map[string]int64) error { 105 containerSet := make(map[string][]types.Container) 106 107 for _, status := range containerHealthStatuses { 108 if err := func() error { 109 ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration) 110 defer cancel() 111 112 v, err := d.client.ContainerList(ctx, types.ContainerListOptions{ 113 All: true, 114 Filters: filters.NewArgs(filters.KeyValuePair{Key: "health", Value: status}), 115 Size: d.CollectContainerSize, 116 }) 117 if err != nil { 118 return err 119 } 120 containerSet[status] = v 121 return nil 122 123 }(); err != nil { 124 return err 125 } 126 } 127 128 seen := make(map[string]bool) 129 130 for _, s := range containerHealthStatuses { 131 mx["containers_health_status_"+s] = 0 132 } 133 mx["containers_health_status_not_running_unhealthy"] = 0 134 135 for status, containers := range containerSet { 136 if status != types.Unhealthy { 137 mx["containers_health_status_"+status] = int64(len(containers)) 138 } 139 140 for _, cntr := range containers { 141 if status == types.Unhealthy { 142 if cntr.State == "running" { 143 mx["containers_health_status_"+status] += 1 144 } else { 145 mx["containers_health_status_not_running_unhealthy"] += 1 146 } 147 } 148 149 if len(cntr.Names) == 0 { 150 continue 151 } 152 153 name := strings.TrimPrefix(cntr.Names[0], "/") 154 155 seen[name] = true 156 157 if !d.containers[name] { 158 d.containers[name] = true 159 d.addContainerCharts(name, cntr.Image) 160 } 161 162 px := fmt.Sprintf("container_%s_", name) 163 164 for _, s := range containerHealthStatuses { 165 mx[px+"health_status_"+s] = 0 166 } 167 mx[px+"health_status_not_running_unhealthy"] = 0 168 for _, s := range containerStates { 169 mx[px+"state_"+s] = 0 170 } 171 172 if status == types.Unhealthy && cntr.State != "running" { 173 mx[px+"health_status_not_running_unhealthy"] += 1 174 } else { 175 mx[px+"health_status_"+status] = 1 176 } 177 mx[px+"state_"+cntr.State] = 1 178 mx[px+"size_rw"] = cntr.SizeRw 179 mx[px+"size_root_fs"] = cntr.SizeRootFs 180 } 181 } 182 183 for name := range d.containers { 184 if !seen[name] { 185 delete(d.containers, name) 186 d.removeContainerCharts(name) 187 } 188 } 189 190 return nil 191 } 192 193 func (d *Docker) negotiateAPIVersion() { 194 ctx, cancel := context.WithTimeout(context.Background(), d.Timeout.Duration) 195 defer cancel() 196 197 d.client.NegotiateAPIVersion(ctx) 198 }