VictoriaMetrics/lib/promscrape/discovery/docker/container.go
Aliaksandr Valialkin a9525da8a4
lib: consistently use f-tests instead of table-driven tests
This makes easier to read and debug these tests. This also reduces test lines count by 15% from 3K to 2.5K
See https://itnext.io/f-tests-as-a-replacement-for-table-driven-tests-in-go-8814a8b19e9e

While at it, consistently use t.Fatal* instead of t.Error*, since t.Error* usually leads
to more complicated and fragile tests, while it doesn't bring any practical benefits over t.Fatal*.
2024-07-09 22:40:50 +02:00

124 lines
3.6 KiB
Go

package docker
import (
"encoding/json"
"fmt"
"strconv"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutils"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
)
// See https://github.com/moby/moby/blob/314759dc2f4745925d8dec6d15acc7761c6e5c92/docs/api/v1.41.yaml#L4024
type container struct {
ID string
Names []string
Labels map[string]string
Ports []containerPort
HostConfig containerHostConfig
NetworkSettings containerNetworkSettings
}
type containerPort struct {
IP string
PrivatePort int
PublicPort int
Type string
}
type containerHostConfig struct {
NetworkMode string
}
type containerNetworkSettings struct {
Networks map[string]containerNetwork
}
type containerNetwork struct {
IPAddress string
NetworkID string
}
func getContainersLabels(cfg *apiConfig) ([]*promutils.Labels, error) {
networkLabels, err := getNetworksLabelsByNetworkID(cfg)
if err != nil {
return nil, err
}
containers, err := getContainers(cfg)
if err != nil {
return nil, err
}
return addContainersLabels(containers, networkLabels, cfg.port, cfg.hostNetworkingHost), nil
}
func getContainers(cfg *apiConfig) ([]container, error) {
resp, err := cfg.getAPIResponse("/containers/json")
if err != nil {
return nil, fmt.Errorf("cannot query dockerd api for containers: %w", err)
}
return parseContainers(resp)
}
func parseContainers(data []byte) ([]container, error) {
var containers []container
if err := json.Unmarshal(data, &containers); err != nil {
return nil, fmt.Errorf("cannot parse containers: %w", err)
}
return containers, nil
}
func addContainersLabels(containers []container, networkLabels map[string]*promutils.Labels, defaultPort int, hostNetworkingHost string) []*promutils.Labels {
var ms []*promutils.Labels
for i := range containers {
c := &containers[i]
if len(c.Names) == 0 {
continue
}
for _, n := range c.NetworkSettings.Networks {
var added bool
for _, p := range c.Ports {
if p.Type != "tcp" {
continue
}
m := promutils.NewLabels(16)
m.Add("__address__", discoveryutils.JoinHostPort(n.IPAddress, p.PrivatePort))
m.Add("__meta_docker_network_ip", n.IPAddress)
m.Add("__meta_docker_port_private", strconv.Itoa(p.PrivatePort))
if p.PublicPort > 0 {
m.Add("__meta_docker_port_public", strconv.Itoa(p.PublicPort))
m.Add("__meta_docker_port_public_ip", p.IP)
}
addCommonLabels(m, c, networkLabels[n.NetworkID])
// Remove possible duplicate labels, which can appear after addCommonLabels() call
m.RemoveDuplicates()
ms = append(ms, m)
added = true
}
if !added {
// Use fallback port when no exposed ports are available or if all are non-TCP
addr := hostNetworkingHost
if c.HostConfig.NetworkMode != "host" {
addr = discoveryutils.JoinHostPort(n.IPAddress, defaultPort)
}
m := promutils.NewLabels(16)
m.Add("__address__", addr)
m.Add("__meta_docker_network_ip", n.IPAddress)
addCommonLabels(m, c, networkLabels[n.NetworkID])
// Remove possible duplicate labels, which can appear after addCommonLabels() call
m.RemoveDuplicates()
ms = append(ms, m)
}
}
}
return ms
}
func addCommonLabels(m *promutils.Labels, c *container, networkLabels *promutils.Labels) {
m.Add("__meta_docker_container_id", c.ID)
m.Add("__meta_docker_container_name", c.Names[0])
m.Add("__meta_docker_container_network_mode", c.HostConfig.NetworkMode)
for k, v := range c.Labels {
m.Add(discoveryutils.SanitizeLabelName("__meta_docker_container_label_"+k), v)
}
m.AddFrom(networkLabels)
}